diff --git a/crates/autopilot/src/infra/solvers/dto/reveal.rs b/crates/autopilot/src/infra/solvers/dto/reveal.rs index 29116152b3..1cd0848919 100644 --- a/crates/autopilot/src/infra/solvers/dto/reveal.rs +++ b/crates/autopilot/src/infra/solvers/dto/reveal.rs @@ -10,6 +10,9 @@ pub struct Request { /// Unique ID of the solution (per driver competition), to reveal. #[serde_as(as = "serde_with::DisplayFromStr")] pub solution_id: u64, + /// Auction ID in which the specified solution ID is competing. + #[serde_as(as = "serde_with::DisplayFromStr")] + pub auction_id: i64, } #[serde_as] diff --git a/crates/autopilot/src/infra/solvers/dto/settle.rs b/crates/autopilot/src/infra/solvers/dto/settle.rs index d6cb6c3d44..3ffba83df3 100644 --- a/crates/autopilot/src/infra/solvers/dto/settle.rs +++ b/crates/autopilot/src/infra/solvers/dto/settle.rs @@ -13,6 +13,9 @@ pub struct Request { pub solution_id: u64, /// The last block number in which the solution TX can be included pub submission_deadline_latest_block: u64, + /// Auction ID in which the specified solution ID is competing. + #[serde_as(as = "serde_with::DisplayFromStr")] + pub auction_id: i64, } #[serde_as] diff --git a/crates/autopilot/src/run_loop.rs b/crates/autopilot/src/run_loop.rs index 1c71df6d7d..3b65f3bc65 100644 --- a/crates/autopilot/src/run_loop.rs +++ b/crates/autopilot/src/run_loop.rs @@ -800,6 +800,7 @@ impl RunLoop { let request = settle::Request { solution_id, submission_deadline_latest_block, + auction_id, }; // Wait for either the settlement transaction to be mined or the driver returned diff --git a/crates/autopilot/src/shadow.rs b/crates/autopilot/src/shadow.rs index 78e148758a..44db5bd203 100644 --- a/crates/autopilot/src/shadow.rs +++ b/crates/autopilot/src/shadow.rs @@ -274,7 +274,10 @@ impl RunLoop { .collect(); let revealed = driver - .reveal(&reveal::Request { solution_id }) + .reveal(&reveal::Request { + solution_id, + auction_id: request.id, + }) .await .map_err(Error::Reveal)?; if !revealed diff --git a/crates/driver/src/domain/competition/mod.rs b/crates/driver/src/domain/competition/mod.rs index da3b3d112f..5fb3cab346 100644 --- a/crates/driver/src/domain/competition/mod.rs +++ b/crates/driver/src/domain/competition/mod.rs @@ -267,13 +267,13 @@ impl Competition { Ok(score) } - pub async fn reveal(&self, solution_id: u64) -> Result { + pub async fn reveal(&self, solution_id: u64, auction_id: i64) -> Result { let settlement = self .settlements .lock() .unwrap() .iter() - .find(|s| s.solution().get() == solution_id) + .find(|s| s.solution().get() == solution_id && s.auction_id.0 == auction_id) .cloned() .ok_or(Error::SolutionNotAvailable)?; Ok(Revealed { @@ -292,6 +292,7 @@ impl Competition { /// [`Competition::solve`] to generate the solution. pub async fn settle( &self, + auction_id: i64, solution_id: u64, submission_deadline: u64, ) -> Result { @@ -299,7 +300,7 @@ impl Competition { let mut lock = self.settlements.lock().unwrap(); let index = lock .iter() - .position(|s| s.solution().get() == solution_id) + .position(|s| s.solution().get() == solution_id && s.auction_id.0 == auction_id) .ok_or(Error::SolutionNotAvailable)?; // remove settlement to ensure we can't settle it twice by accident lock.swap_remove_front(index) diff --git a/crates/driver/src/infra/api/routes/reveal/dto/solution.rs b/crates/driver/src/infra/api/routes/reveal/dto/solution.rs index 150a73e090..60e8f564bc 100644 --- a/crates/driver/src/infra/api/routes/reveal/dto/solution.rs +++ b/crates/driver/src/infra/api/routes/reveal/dto/solution.rs @@ -7,4 +7,7 @@ pub struct Solution { /// Unique ID of the solution (per driver competition), to reveal. #[serde_as(as = "serde_with::DisplayFromStr")] pub solution_id: u64, + /// Auction ID in which the specified solution ID is competing. + #[serde_as(as = "serde_with::DisplayFromStr")] + pub auction_id: i64, } diff --git a/crates/driver/src/infra/api/routes/reveal/mod.rs b/crates/driver/src/infra/api/routes/reveal/mod.rs index 5d5cd8c769..750636cdaf 100644 --- a/crates/driver/src/infra/api/routes/reveal/mod.rs +++ b/crates/driver/src/infra/api/routes/reveal/mod.rs @@ -16,17 +16,18 @@ async fn route( state: axum::extract::State, req: axum::Json, ) -> Result, (hyper::StatusCode, axum::Json)> { - let competition = state.competition(); - let auction_id = competition.auction_id(req.solution_id).map(|id| id.0); let handle_request = async { observe::revealing(); - let result = competition.reveal(req.solution_id).await; + let result = state + .competition() + .reveal(req.solution_id, req.auction_id) + .await; observe::revealed(state.solver().name(), &result); let result = result?; Ok(axum::Json(dto::Revealed::new(result))) }; handle_request - .instrument(tracing::info_span!("/reveal", solver = %state.solver().name(), auction_id)) + .instrument(tracing::info_span!("/reveal", solver = %state.solver().name(), req.auction_id)) .await } diff --git a/crates/driver/src/infra/api/routes/settle/dto/solution.rs b/crates/driver/src/infra/api/routes/settle/dto/solution.rs index e5d2534139..ab2a52e2ec 100644 --- a/crates/driver/src/infra/api/routes/settle/dto/solution.rs +++ b/crates/driver/src/infra/api/routes/settle/dto/solution.rs @@ -9,4 +9,7 @@ pub struct Solution { pub solution_id: u64, /// The last block number in which the solution TX can be included pub submission_deadline_latest_block: u64, + /// Auction ID in which this solution is competing. + #[serde_as(as = "serde_with::DisplayFromStr")] + pub auction_id: i64, } diff --git a/crates/driver/src/infra/api/routes/settle/mod.rs b/crates/driver/src/infra/api/routes/settle/mod.rs index c45fb52df3..b75bdb7435 100644 --- a/crates/driver/src/infra/api/routes/settle/mod.rs +++ b/crates/driver/src/infra/api/routes/settle/mod.rs @@ -19,18 +19,18 @@ async fn route( state: axum::extract::State, req: axum::Json, ) -> Result<(), (hyper::StatusCode, axum::Json)> { - let state = state.clone(); - let auction_id = state - .competition() - .auction_id(req.solution_id) - .map(|id| id.0); + let auction_id = req.auction_id; let solver = state.solver().name().to_string(); let handle_request = async move { observe::settling(); let result = state .competition() - .settle(req.solution_id, req.submission_deadline_latest_block) + .settle( + req.auction_id, + req.solution_id, + req.submission_deadline_latest_block, + ) .await; observe::settled(state.solver().name(), &result); result.map(|_| ()).map_err(Into::into) diff --git a/crates/driver/src/tests/cases/parallel_auctions.rs b/crates/driver/src/tests/cases/parallel_auctions.rs index ea7026a41f..12445d2f8b 100644 --- a/crates/driver/src/tests/cases/parallel_auctions.rs +++ b/crates/driver/src/tests/cases/parallel_auctions.rs @@ -13,28 +13,53 @@ use crate::tests::setup::{eth_order, eth_solution, setup, weth_pool}; #[ignore] async fn driver_handles_solutions_based_on_id() { let order = eth_order(); - let test = setup() + let mut test = setup() .pool(weth_pool()) .order(order.clone()) .solution(eth_solution()) + .auction_id(1) .done() .await; - let id = test.solve().await.ok().id(); + let solution_id = test.solve().await.ok().id(); // calling `/reveal` or `/settle` with incorrect solution ids // results in an error. test.settle("99").await.err().kind("SolutionNotAvailable"); test.reveal("99").await.err().kind("SolutionNotAvailable"); + // calling `/reveal` or `/settle` with a reasonable id works + // but wrong auction id results in an error. + test.set_auction_id(100); + test.reveal(&solution_id) + .await + .err() + .kind("SolutionNotAvailable"); + test.settle(&solution_id) + .await + .err() + .kind("SolutionNotAvailable"); + // calling `/reveal` or `/settle` with a reasonable id works. - test.reveal(&id).await.ok(); - test.settle(&id).await.ok().await.eth_order_executed().await; + test.set_auction_id(1); + test.reveal(&solution_id).await.ok(); + test.settle(&solution_id) + .await + .ok() + .await + .eth_order_executed() + .await; // calling `/reveal` or `/settle` with for a legit solution that // has already been settled also fails. - test.settle(&id).await.err().kind("SolutionNotAvailable"); - test.reveal(&id).await.err().kind("SolutionNotAvailable"); + test.settle(&solution_id) + .await + .err() + .kind("SolutionNotAvailable"); + test.reveal(&solution_id) + .await + .err() + .kind("SolutionNotAvailable"); } /// Tests that the driver can correctly settle a solution that diff --git a/crates/driver/src/tests/setup/driver.rs b/crates/driver/src/tests/setup/driver.rs index 388ecd10e1..6cf08b1cc6 100644 --- a/crates/driver/src/tests/setup/driver.rs +++ b/crates/driver/src/tests/setup/driver.rs @@ -138,7 +138,7 @@ pub fn solve_req(test: &Test) -> serde_json::Value { } } json!({ - "id": "1", + "id": test.auction_id.to_string(), "tokens": tokens_json, "orders": orders_json, "deadline": test.deadline, @@ -147,17 +147,23 @@ pub fn solve_req(test: &Test) -> serde_json::Value { } /// Create a request for the driver /reveal endpoint. -pub fn reveal_req(solution_id: &str) -> serde_json::Value { +pub fn reveal_req(solution_id: &str, auction_id: &str) -> serde_json::Value { json!({ "solutionId": solution_id, + "auctionId": auction_id, }) } /// Create a request for the driver /settle endpoint. -pub fn settle_req(submission_deadline_latest_block: u64, solution_id: &str) -> serde_json::Value { +pub fn settle_req( + submission_deadline_latest_block: u64, + solution_id: &str, + auction_id: &str, +) -> serde_json::Value { json!({ "solutionId": solution_id, "submissionDeadlineLatestBlock": submission_deadline_latest_block, + "auctionId": auction_id, }) } diff --git a/crates/driver/src/tests/setup/mod.rs b/crates/driver/src/tests/setup/mod.rs index 8dba9197b0..63458971b0 100644 --- a/crates/driver/src/tests/setup/mod.rs +++ b/crates/driver/src/tests/setup/mod.rs @@ -493,26 +493,17 @@ pub enum Mempool { /// Create a builder for the setup process. pub fn setup() -> Setup { Setup { - name: Default::default(), - pools: Default::default(), - orders: Default::default(), - order_priority_strategies: Default::default(), - trusted: Default::default(), - config_file: Default::default(), - solutions: Default::default(), - quote: Default::default(), solvers: vec![test_solver()], enable_simulation: true, - settlement_address: Default::default(), mempools: vec![Mempool::Public], rpc_args: vec!["--gas-limit".into(), "10000000".into()], - jit_orders: Default::default(), - surplus_capturing_jit_order_owners: Default::default(), allow_multiple_solve_requests: false, + auction_id: 1, + ..Default::default() } } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Setup { name: Option, pools: Vec, @@ -539,6 +530,8 @@ pub struct Setup { surplus_capturing_jit_order_owners: Vec, /// In case your test requires multiple `/solve` requests allow_multiple_solve_requests: bool, + /// Auction ID used during tests + auction_id: i64, } /// The validity of a solution. @@ -839,6 +832,16 @@ impl Setup { self } + /// Set specific auction ID which will be used during test. + /// Setting auction ID can be used to test various scenarios when handling + /// competing solutions in autopilot, and for interface tests between + /// autopilot and solvers. + /// By default auction ID is set to 1. + pub fn auction_id(mut self, auction_id: i64) -> Self { + self.auction_id = auction_id; + self + } + /// Create the test: set up onchain contracts and pools, start a mock HTTP /// server for the solver and start the HTTP server for the driver. pub async fn done(self) -> Test { @@ -959,6 +962,7 @@ impl Setup { quoted_orders: quotes, quote: self.quote, surplus_capturing_jit_order_owners, + auction_id: self.auction_id, } } @@ -999,6 +1003,7 @@ pub struct Test { quote: bool, /// List of surplus capturing JIT-order owners surplus_capturing_jit_order_owners: Vec, + auction_id: i64, } impl Test { @@ -1035,7 +1040,10 @@ impl Test { self.driver.addr, solver::NAME )) - .json(&driver::reveal_req(solution_id)) + .json(&driver::reveal_req( + solution_id, + &self.auction_id.to_string(), + )) .send() .await .unwrap(); @@ -1094,6 +1102,7 @@ impl Test { .json(&driver::settle_req( submission_deadline_latest_block, solution_id, + &self.auction_id.to_string(), )) .send() .await @@ -1149,6 +1158,14 @@ impl Test { pub fn web3(&self) -> &web3::Web3 { &self.blockchain.web3 } + + /// Changes auction ID for current test. + /// Can be used in autopilot/solver related test cases to + /// test context changes for competing solutions. + /// Default value is set by Setup builder. + pub fn set_auction_id(&mut self, auction_id: i64) { + self.auction_id = auction_id; + } } /// A /solve response. diff --git a/crates/driver/src/tests/setup/solver.rs b/crates/driver/src/tests/setup/solver.rs index 2f9cbe28cc..777e6416d2 100644 --- a/crates/driver/src/tests/setup/solver.rs +++ b/crates/driver/src/tests/setup/solver.rs @@ -432,7 +432,7 @@ impl Solver { .0 .to_string(); let expected = json!({ - "id": if config.quote { None } else { Some("1") }, + "id": (!config.quote).then_some("1" ), "tokens": tokens_json, "orders": orders_json, "liquidity": [],