From 745f15ae356cefaaaed9e7723c89cc699c7d4b40 Mon Sep 17 00:00:00 2001 From: ilya Date: Thu, 13 Feb 2025 20:49:45 +0000 Subject: [PATCH 1/8] Solver participation guard e2e test --- crates/e2e/src/setup/services.rs | 2 +- crates/e2e/tests/e2e/main.rs | 1 + .../tests/e2e/solver_participation_guard.rs | 305 ++++++++++++++++++ 3 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 crates/e2e/tests/e2e/solver_participation_guard.rs diff --git a/crates/e2e/src/setup/services.rs b/crates/e2e/src/setup/services.rs index a8647b9a21..779cd68d87 100644 --- a/crates/e2e/src/setup/services.rs +++ b/crates/e2e/src/setup/services.rs @@ -224,7 +224,7 @@ impl<'a> Services<'a> { [ vec![ format!( - "--drivers=test_solver|http://localhost:11088/test_solver|{}", + "--drivers=test_solver|http://localhost:11088/test_solver|{}|true", hex::encode(solver.address()) ), "--price-estimation-drivers=test_quoter|http://localhost:11088/test_solver" diff --git a/crates/e2e/tests/e2e/main.rs b/crates/e2e/tests/e2e/main.rs index e525829381..5ce4fd3f3f 100644 --- a/crates/e2e/tests/e2e/main.rs +++ b/crates/e2e/tests/e2e/main.rs @@ -29,6 +29,7 @@ mod refunder; mod replace_order; mod smart_contract_orders; mod solver_competition; +mod solver_participation_guard; mod submission; mod tracking_insufficient_funds; mod uncovered_order; diff --git a/crates/e2e/tests/e2e/solver_participation_guard.rs b/crates/e2e/tests/e2e/solver_participation_guard.rs new file mode 100644 index 0000000000..8af189c5ac --- /dev/null +++ b/crates/e2e/tests/e2e/solver_participation_guard.rs @@ -0,0 +1,305 @@ +use { + e2e::{ + setup::{ + run_test, + to_wei, + wait_for_condition, + Db, + ExtraServiceArgs, + MintableToken, + OnchainComponents, + Services, + TestAccount, + TIMEOUT, + }, + tx, + }, + ethrpc::Web3, + model::{ + order::{OrderClass, OrderCreation, OrderKind}, + signature::EcdsaSigningScheme, + }, + secp256k1::SecretKey, + sqlx::Row, + web3::{ + signing::SecretKeyRef, + types::{H160, U256}, + }, +}; + +#[tokio::test] +#[ignore] +async fn local_node_non_settling_solver() { + run_test(non_settling_solver).await; +} + +#[tokio::test] +#[ignore] +async fn local_node_low_settling_solver() { + run_test(low_settling_solver).await; +} + +async fn non_settling_solver(web3: Web3) { + let mut onchain = OnchainComponents::deploy(web3.clone()).await; + + let [solver, solver_b] = onchain.make_solvers(to_wei(1)).await; + let [trader_a] = onchain.make_accounts(to_wei(1)).await; + let [token_a, token_b] = onchain + .deploy_tokens_with_weth_uni_v2_pools(to_wei(1_000), to_wei(1_000)) + .await; + + // Fund trader accounts + token_a.mint(trader_a.address(), to_wei(1000)).await; + + // Create and fund Uniswap pool + token_a.mint(solver.address(), to_wei(1000)).await; + token_b.mint(solver.address(), to_wei(1000)).await; + tx!( + solver.account(), + onchain + .contracts() + .uniswap_v2_factory + .create_pair(token_a.address(), token_b.address()) + ); + tx!( + solver.account(), + token_a.approve( + onchain.contracts().uniswap_v2_router.address(), + to_wei(1000) + ) + ); + tx!( + solver.account(), + token_b.approve( + onchain.contracts().uniswap_v2_router.address(), + to_wei(1000) + ) + ); + tx!( + solver.account(), + onchain.contracts().uniswap_v2_router.add_liquidity( + token_a.address(), + token_b.address(), + to_wei(1000), + to_wei(1000), + 0_u64.into(), + 0_u64.into(), + solver.address(), + U256::max_value(), + ) + ); + + // Approve GPv2 for trading + tx!( + trader_a.account(), + token_a.approve(onchain.contracts().allowance, to_wei(1000)) + ); + + // Place Orders + let services = Services::new(&onchain).await; + services.start_protocol(solver).await; + + for _ in 0..4 { + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .unwrap(); + } + + let pool = services.db(); + let settled_auction_ids = fetch_last_settled_auction_ids(pool).await; + assert_eq!(settled_auction_ids.len(), 4); + // Build 5 blocks to make sure the submission deadline is passed, which is 5 by + // default. + for _ in 0..5 { + onchain.mint_block().await; + } + + // Simulate failed settlements by replacing the solver for the last 3 + // settlements. + let last_auctions = settled_auction_ids + .iter() + .take(3) + .cloned() + .collect::>(); + replace_solver_for_auction_ids(pool, &last_auctions, &solver_b.address()).await; + // The competition still passes since the stats are updated only after a new + // solution from anyone is received and stored. + assert!( + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .is_ok() + ); + // Now, the stat is updated, and the solver is banned. + assert!( + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .is_err() + ); +} + +async fn low_settling_solver(web3: Web3) { + let mut onchain = OnchainComponents::deploy(web3.clone()).await; + + let [solver, solver_b] = onchain.make_solvers(to_wei(1)).await; + let [trader_a] = onchain.make_accounts(to_wei(1)).await; + let [token_a, token_b] = onchain + .deploy_tokens_with_weth_uni_v2_pools(to_wei(1_000), to_wei(1_000)) + .await; + + // Fund trader accounts + token_a.mint(trader_a.address(), to_wei(1000)).await; + + // Create and fund Uniswap pool + token_a.mint(solver.address(), to_wei(1000)).await; + token_b.mint(solver.address(), to_wei(1000)).await; + tx!( + solver.account(), + onchain + .contracts() + .uniswap_v2_factory + .create_pair(token_a.address(), token_b.address()) + ); + tx!( + solver.account(), + token_a.approve( + onchain.contracts().uniswap_v2_router.address(), + to_wei(1000) + ) + ); + tx!( + solver.account(), + token_b.approve( + onchain.contracts().uniswap_v2_router.address(), + to_wei(1000) + ) + ); + tx!( + solver.account(), + onchain.contracts().uniswap_v2_router.add_liquidity( + token_a.address(), + token_b.address(), + to_wei(1000), + to_wei(1000), + 0_u64.into(), + 0_u64.into(), + solver.address(), + U256::max_value(), + ) + ); + + // Approve GPv2 for trading + tx!( + trader_a.account(), + token_a.approve(onchain.contracts().allowance, to_wei(1000)) + ); + + let services = Services::new(&onchain).await; + let args = ExtraServiceArgs { + // The solver is banned if the failure settlement rate is above 55%. + autopilot: vec!["--solver-max-settlement-failure-rate=0.55".to_string()], + ..Default::default() + }; + services.start_protocol_with_args(args, solver).await; + + for _ in 0..5 { + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .unwrap(); + } + + let pool = services.db(); + let settled_auction_ids = fetch_last_settled_auction_ids(pool).await; + assert_eq!(settled_auction_ids.len(), 5); + // Build 5 blocks to make sure the submission deadline is passed, which is 5 by + // default. + for _ in 0..5 { + onchain.mint_block().await; + } + + // Simulate low settling rate by replacing the solver for the 60% of the + // settlements. + let random_auctions = settled_auction_ids + .iter() + .enumerate() + .filter_map(|(i, id)| (i % 2 == 0).then_some(*id)) + .collect::>(); + tracing::info!("newlog random_auctions={:?}", random_auctions); + replace_solver_for_auction_ids(pool, &random_auctions, &solver_b.address()).await; + // The competition still passes since the stats are updated only after a new + // solution from anyone is received and stored. + assert!( + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .is_ok() + ); + // Now, the stat is updated, and the solver is banned. + assert!( + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .is_err() + ); +} + +async fn replace_solver_for_auction_ids(pool: &Db, auction_ids: &[i64], solver: &H160) { + for auction_id in auction_ids { + sqlx::query("UPDATE settlements SET solver = $1 WHERE auction_id = $2") + .bind(solver.0) + .bind(auction_id) + .execute(pool) + .await + .unwrap(); + } +} + +async fn fetch_last_settled_auction_ids(pool: &Db) -> Vec { + sqlx::query("SELECT auction_id FROM settlements ORDER BY auction_id DESC") + .fetch_all(pool) + .await + .unwrap() + .into_iter() + .filter_map(|row| { + let auction_id: Option = row.try_get(0).unwrap(); + auction_id + }) + .collect() +} + +async fn execute_order( + onchain: &OnchainComponents, + trader_a: &TestAccount, + token_a: &MintableToken, + token_b: &MintableToken, + services: &Services<'_>, +) -> anyhow::Result<()> { + let order = OrderCreation { + sell_token: token_a.address(), + sell_amount: to_wei(10), + buy_token: token_b.address(), + buy_amount: to_wei(5), + valid_to: model::time::now_in_epoch_seconds() + 300, + kind: OrderKind::Sell, + ..Default::default() + } + .sign( + EcdsaSigningScheme::Eip712, + &onchain.contracts().domain_separator, + SecretKeyRef::from(&SecretKey::from_slice(trader_a.private_key()).unwrap()), + ); + let balance_before = token_b.balance_of(trader_a.address()).call().await.unwrap(); + let order_id = services.create_order(&order).await.unwrap(); + onchain.mint_block().await; + let limit_order = services.get_order(&order_id).await.unwrap(); + assert_eq!(limit_order.metadata.class, OrderClass::Limit); + let auction_ids_before = fetch_last_settled_auction_ids(services.db()).await.len(); + + // Drive solution + tracing::info!("Waiting for trade."); + wait_for_condition(TIMEOUT, || async { + let balance_after = token_b.balance_of(trader_a.address()).call().await.unwrap(); + let balance_changes = balance_after.checked_sub(balance_before).unwrap() >= to_wei(5); + let auction_ids_after = + fetch_last_settled_auction_ids(services.db()).await.len() > auction_ids_before; + balance_changes && auction_ids_after + }) + .await +} From c4db1b7d4534c04c1a2b49b4b824f4a505e9178b Mon Sep 17 00:00:00 2001 From: ilya Date: Thu, 13 Feb 2025 20:56:11 +0000 Subject: [PATCH 2/8] Onchain case --- .../tests/e2e/solver_participation_guard.rs | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/crates/e2e/tests/e2e/solver_participation_guard.rs b/crates/e2e/tests/e2e/solver_participation_guard.rs index 8af189c5ac..ecd948bff5 100644 --- a/crates/e2e/tests/e2e/solver_participation_guard.rs +++ b/crates/e2e/tests/e2e/solver_participation_guard.rs @@ -39,6 +39,12 @@ async fn local_node_low_settling_solver() { run_test(low_settling_solver).await; } +#[tokio::test] +#[ignore] +async fn local_node_not_allowed_solver() { + run_test(not_allowed_solver).await; +} + async fn non_settling_solver(web3: Web3) { let mut onchain = OnchainComponents::deploy(web3.clone()).await; @@ -95,7 +101,6 @@ async fn non_settling_solver(web3: Web3) { token_a.approve(onchain.contracts().allowance, to_wei(1000)) ); - // Place Orders let services = Services::new(&onchain).await; services.start_protocol(solver).await; @@ -240,6 +245,83 @@ async fn low_settling_solver(web3: Web3) { ); } +async fn not_allowed_solver(web3: Web3) { + let mut onchain = OnchainComponents::deploy(web3.clone()).await; + + let [solver] = onchain.make_solvers(to_wei(1)).await; + let [trader_a] = onchain.make_accounts(to_wei(1)).await; + let [token_a, token_b] = onchain + .deploy_tokens_with_weth_uni_v2_pools(to_wei(1_000), to_wei(1_000)) + .await; + + // Fund trader accounts + token_a.mint(trader_a.address(), to_wei(1000)).await; + + // Create and fund Uniswap pool + token_a.mint(solver.address(), to_wei(1000)).await; + token_b.mint(solver.address(), to_wei(1000)).await; + tx!( + solver.account(), + onchain + .contracts() + .uniswap_v2_factory + .create_pair(token_a.address(), token_b.address()) + ); + tx!( + solver.account(), + token_a.approve( + onchain.contracts().uniswap_v2_router.address(), + to_wei(1000) + ) + ); + tx!( + solver.account(), + token_b.approve( + onchain.contracts().uniswap_v2_router.address(), + to_wei(1000) + ) + ); + tx!( + solver.account(), + onchain.contracts().uniswap_v2_router.add_liquidity( + token_a.address(), + token_b.address(), + to_wei(1000), + to_wei(1000), + 0_u64.into(), + 0_u64.into(), + solver.address(), + U256::max_value(), + ) + ); + + // Approve GPv2 for trading + tx!( + trader_a.account(), + token_a.approve(onchain.contracts().allowance, to_wei(1000)) + ); + + let solver_address = solver.address(); + let services = Services::new(&onchain).await; + services.start_protocol(solver).await; + + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .unwrap(); + + // Ban the solver + onchain + .contracts() + .gp_authenticator + .methods() + .remove_solver(solver_address) + .send() + .await + .unwrap(); + + assert!(execute_order(&onchain, &trader_a, &token_a, &token_b, &services).await.is_err()); +} + async fn replace_solver_for_auction_ids(pool: &Db, auction_ids: &[i64], solver: &H160) { for auction_id in auction_ids { sqlx::query("UPDATE settlements SET solver = $1 WHERE auction_id = $2") From 97e5846a566f8b83968030f72ebc9e9384db48b3 Mon Sep 17 00:00:00 2001 From: ilya Date: Thu, 13 Feb 2025 20:56:35 +0000 Subject: [PATCH 3/8] Formatting --- crates/e2e/tests/e2e/solver_participation_guard.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/e2e/tests/e2e/solver_participation_guard.rs b/crates/e2e/tests/e2e/solver_participation_guard.rs index ecd948bff5..56424f1ae3 100644 --- a/crates/e2e/tests/e2e/solver_participation_guard.rs +++ b/crates/e2e/tests/e2e/solver_participation_guard.rs @@ -319,7 +319,11 @@ async fn not_allowed_solver(web3: Web3) { .await .unwrap(); - assert!(execute_order(&onchain, &trader_a, &token_a, &token_b, &services).await.is_err()); + assert!( + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .is_err() + ); } async fn replace_solver_for_auction_ids(pool: &Db, auction_ids: &[i64], solver: &H160) { From 29db852f87167836d7da664b28bb49234306f8eb Mon Sep 17 00:00:00 2001 From: ilya Date: Mon, 17 Feb 2025 17:40:32 +0000 Subject: [PATCH 4/8] Config update --- crates/e2e/src/setup/services.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/e2e/src/setup/services.rs b/crates/e2e/src/setup/services.rs index 779cd68d87..795cd93474 100644 --- a/crates/e2e/src/setup/services.rs +++ b/crates/e2e/src/setup/services.rs @@ -224,7 +224,7 @@ impl<'a> Services<'a> { [ vec![ format!( - "--drivers=test_solver|http://localhost:11088/test_solver|{}|true", + "--drivers=test_solver|http://localhost:11088/test_solver|{}|requested_timeout_on_problems", hex::encode(solver.address()) ), "--price-estimation-drivers=test_quoter|http://localhost:11088/test_solver" From d1ee1e567fdebc8eb88a10c689aae535df264d85 Mon Sep 17 00:00:00 2001 From: ilya Date: Mon, 17 Feb 2025 17:55:06 +0000 Subject: [PATCH 5/8] Minor refactoring --- .../tests/e2e/solver_participation_guard.rs | 162 ++++-------------- 1 file changed, 37 insertions(+), 125 deletions(-) diff --git a/crates/e2e/tests/e2e/solver_participation_guard.rs b/crates/e2e/tests/e2e/solver_participation_guard.rs index 56424f1ae3..7ffc98494e 100644 --- a/crates/e2e/tests/e2e/solver_participation_guard.rs +++ b/crates/e2e/tests/e2e/solver_participation_guard.rs @@ -49,61 +49,13 @@ async fn non_settling_solver(web3: Web3) { let mut onchain = OnchainComponents::deploy(web3.clone()).await; let [solver, solver_b] = onchain.make_solvers(to_wei(1)).await; - let [trader_a] = onchain.make_accounts(to_wei(1)).await; - let [token_a, token_b] = onchain - .deploy_tokens_with_weth_uni_v2_pools(to_wei(1_000), to_wei(1_000)) - .await; - - // Fund trader accounts - token_a.mint(trader_a.address(), to_wei(1000)).await; - - // Create and fund Uniswap pool - token_a.mint(solver.address(), to_wei(1000)).await; - token_b.mint(solver.address(), to_wei(1000)).await; - tx!( - solver.account(), - onchain - .contracts() - .uniswap_v2_factory - .create_pair(token_a.address(), token_b.address()) - ); - tx!( - solver.account(), - token_a.approve( - onchain.contracts().uniswap_v2_router.address(), - to_wei(1000) - ) - ); - tx!( - solver.account(), - token_b.approve( - onchain.contracts().uniswap_v2_router.address(), - to_wei(1000) - ) - ); - tx!( - solver.account(), - onchain.contracts().uniswap_v2_router.add_liquidity( - token_a.address(), - token_b.address(), - to_wei(1000), - to_wei(1000), - 0_u64.into(), - 0_u64.into(), - solver.address(), - U256::max_value(), - ) - ); - - // Approve GPv2 for trading - tx!( - trader_a.account(), - token_a.approve(onchain.contracts().allowance, to_wei(1000)) - ); + let (trader_a, token_a, token_b) = setup(&mut onchain, &solver).await; let services = Services::new(&onchain).await; services.start_protocol(solver).await; + // Amount of order should be more or equal the non-settling threshold, which is + // 3. for _ in 0..4 { execute_order(&onchain, &trader_a, &token_a, &token_b, &services) .await @@ -146,57 +98,7 @@ async fn low_settling_solver(web3: Web3) { let mut onchain = OnchainComponents::deploy(web3.clone()).await; let [solver, solver_b] = onchain.make_solvers(to_wei(1)).await; - let [trader_a] = onchain.make_accounts(to_wei(1)).await; - let [token_a, token_b] = onchain - .deploy_tokens_with_weth_uni_v2_pools(to_wei(1_000), to_wei(1_000)) - .await; - - // Fund trader accounts - token_a.mint(trader_a.address(), to_wei(1000)).await; - - // Create and fund Uniswap pool - token_a.mint(solver.address(), to_wei(1000)).await; - token_b.mint(solver.address(), to_wei(1000)).await; - tx!( - solver.account(), - onchain - .contracts() - .uniswap_v2_factory - .create_pair(token_a.address(), token_b.address()) - ); - tx!( - solver.account(), - token_a.approve( - onchain.contracts().uniswap_v2_router.address(), - to_wei(1000) - ) - ); - tx!( - solver.account(), - token_b.approve( - onchain.contracts().uniswap_v2_router.address(), - to_wei(1000) - ) - ); - tx!( - solver.account(), - onchain.contracts().uniswap_v2_router.add_liquidity( - token_a.address(), - token_b.address(), - to_wei(1000), - to_wei(1000), - 0_u64.into(), - 0_u64.into(), - solver.address(), - U256::max_value(), - ) - ); - - // Approve GPv2 for trading - tx!( - trader_a.account(), - token_a.approve(onchain.contracts().allowance, to_wei(1000)) - ); + let (trader_a, token_a, token_b) = setup(&mut onchain, &solver).await; let services = Services::new(&onchain).await; let args = ExtraServiceArgs { @@ -206,6 +108,7 @@ async fn low_settling_solver(web3: Web3) { }; services.start_protocol_with_args(args, solver).await; + // Create 5 orders, to easily test 60% of them failing, which is 3/5. for _ in 0..5 { execute_order(&onchain, &trader_a, &token_a, &token_b, &services) .await @@ -249,6 +152,37 @@ async fn not_allowed_solver(web3: Web3) { let mut onchain = OnchainComponents::deploy(web3.clone()).await; let [solver] = onchain.make_solvers(to_wei(1)).await; + let (trader_a, token_a, token_b) = setup(&mut onchain, &solver).await; + + let solver_address = solver.address(); + let services = Services::new(&onchain).await; + services.start_protocol(solver).await; + + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .unwrap(); + + // Ban the solver + onchain + .contracts() + .gp_authenticator + .methods() + .remove_solver(solver_address) + .send() + .await + .unwrap(); + + assert!( + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .is_err() + ); +} + +async fn setup( + onchain: &mut OnchainComponents, + solver: &TestAccount, +) -> (TestAccount, MintableToken, MintableToken) { let [trader_a] = onchain.make_accounts(to_wei(1)).await; let [token_a, token_b] = onchain .deploy_tokens_with_weth_uni_v2_pools(to_wei(1_000), to_wei(1_000)) @@ -301,29 +235,7 @@ async fn not_allowed_solver(web3: Web3) { token_a.approve(onchain.contracts().allowance, to_wei(1000)) ); - let solver_address = solver.address(); - let services = Services::new(&onchain).await; - services.start_protocol(solver).await; - - execute_order(&onchain, &trader_a, &token_a, &token_b, &services) - .await - .unwrap(); - - // Ban the solver - onchain - .contracts() - .gp_authenticator - .methods() - .remove_solver(solver_address) - .send() - .await - .unwrap(); - - assert!( - execute_order(&onchain, &trader_a, &token_a, &token_b, &services) - .await - .is_err() - ); + (trader_a, token_a, token_b) } async fn replace_solver_for_auction_ids(pool: &Db, auction_ids: &[i64], solver: &H160) { From 930a80afe4e4554ad6f60eb9e26f4880f66031f0 Mon Sep 17 00:00:00 2001 From: ilya Date: Tue, 18 Feb 2025 15:52:29 +0000 Subject: [PATCH 6/8] Extend the test --- crates/e2e/tests/e2e/solver_participation_guard.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/e2e/tests/e2e/solver_participation_guard.rs b/crates/e2e/tests/e2e/solver_participation_guard.rs index 7ffc98494e..b2629f290f 100644 --- a/crates/e2e/tests/e2e/solver_participation_guard.rs +++ b/crates/e2e/tests/e2e/solver_participation_guard.rs @@ -177,6 +177,20 @@ async fn not_allowed_solver(web3: Web3) { .await .is_err() ); + + // Unban the solver + onchain + .contracts() + .gp_authenticator + .methods() + .add_solver(solver_address) + .send() + .await + .unwrap(); + + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .unwrap(); } async fn setup( From afa5326d3cf6fbee6a6b66313a73a99a8224178b Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 19 Feb 2025 16:55:37 +0000 Subject: [PATCH 7/8] Redundant log --- crates/e2e/tests/e2e/solver_participation_guard.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/e2e/tests/e2e/solver_participation_guard.rs b/crates/e2e/tests/e2e/solver_participation_guard.rs index b2629f290f..a53abefb6f 100644 --- a/crates/e2e/tests/e2e/solver_participation_guard.rs +++ b/crates/e2e/tests/e2e/solver_participation_guard.rs @@ -131,7 +131,6 @@ async fn low_settling_solver(web3: Web3) { .enumerate() .filter_map(|(i, id)| (i % 2 == 0).then_some(*id)) .collect::>(); - tracing::info!("newlog random_auctions={:?}", random_auctions); replace_solver_for_auction_ids(pool, &random_auctions, &solver_b.address()).await; // The competition still passes since the stats are updated only after a new // solution from anyone is received and stored. From 8082206c06f03c29ac03d4dbae3d64f840b4ee0c Mon Sep 17 00:00:00 2001 From: ilya Date: Wed, 19 Feb 2025 17:14:29 +0000 Subject: [PATCH 8/8] Check the cache expiration --- .../tests/e2e/solver_participation_guard.rs | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/crates/e2e/tests/e2e/solver_participation_guard.rs b/crates/e2e/tests/e2e/solver_participation_guard.rs index a53abefb6f..dfd726a4f6 100644 --- a/crates/e2e/tests/e2e/solver_participation_guard.rs +++ b/crates/e2e/tests/e2e/solver_participation_guard.rs @@ -21,6 +21,7 @@ use { }, secp256k1::SecretKey, sqlx::Row, + std::time::Instant, web3::{ signing::SecretKeyRef, types::{H160, U256}, @@ -52,7 +53,12 @@ async fn non_settling_solver(web3: Web3) { let (trader_a, token_a, token_b) = setup(&mut onchain, &solver).await; let services = Services::new(&onchain).await; - services.start_protocol(solver).await; + let args = ExtraServiceArgs { + // The solver gets banned for 40s. + autopilot: vec!["--solver-blacklist-cache-ttl=40s".to_string()], + ..Default::default() + }; + services.start_protocol_with_args(args, solver).await; // Amount of order should be more or equal the non-settling threshold, which is // 3. @@ -81,6 +87,7 @@ async fn non_settling_solver(web3: Web3) { replace_solver_for_auction_ids(pool, &last_auctions, &solver_b.address()).await; // The competition still passes since the stats are updated only after a new // solution from anyone is received and stored. + let now = Instant::now(); assert!( execute_order(&onchain, &trader_a, &token_a, &token_b, &services) .await @@ -92,6 +99,19 @@ async fn non_settling_solver(web3: Web3) { .await .is_err() ); + + // 40 seconds is the cache TTL, and 5 seconds is added to compensate any + // possible delays. + let sleep_timeout_secs = 40 - now.elapsed().as_secs() + 5; + println!( + "Sleeping for {} seconds to reset the solver participation guard cache", + sleep_timeout_secs + ); + tokio::time::sleep(tokio::time::Duration::from_secs(sleep_timeout_secs)).await; + // The cache is reset, and the solver is allowed to participate again. + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .unwrap(); } async fn low_settling_solver(web3: Web3) { @@ -102,8 +122,12 @@ async fn low_settling_solver(web3: Web3) { let services = Services::new(&onchain).await; let args = ExtraServiceArgs { - // The solver is banned if the failure settlement rate is above 55%. - autopilot: vec!["--solver-max-settlement-failure-rate=0.55".to_string()], + autopilot: vec![ + // The solver gets banned for 40s. + "--solver-blacklist-cache-ttl=40s".to_string(), + // The solver is banned if the failure settlement rate is above 55%. + "--solver-max-settlement-failure-rate=0.55".to_string(), + ], ..Default::default() }; services.start_protocol_with_args(args, solver).await; @@ -134,6 +158,7 @@ async fn low_settling_solver(web3: Web3) { replace_solver_for_auction_ids(pool, &random_auctions, &solver_b.address()).await; // The competition still passes since the stats are updated only after a new // solution from anyone is received and stored. + let now = Instant::now(); assert!( execute_order(&onchain, &trader_a, &token_a, &token_b, &services) .await @@ -145,6 +170,19 @@ async fn low_settling_solver(web3: Web3) { .await .is_err() ); + + // 40 seconds is the cache TTL, and 5 seconds is added to compensate any + // possible delays. + let sleep_timeout_secs = 40 - now.elapsed().as_secs() + 5; + println!( + "Sleeping for {} seconds to reset the solver participation guard cache", + sleep_timeout_secs + ); + tokio::time::sleep(tokio::time::Duration::from_secs(sleep_timeout_secs)).await; + // The cache is reset, and the solver is allowed to participate again. + execute_order(&onchain, &trader_a, &token_a, &token_b, &services) + .await + .unwrap(); } async fn not_allowed_solver(web3: Web3) {