diff --git a/crates/autopilot/src/database.rs b/crates/autopilot/src/database.rs index 319bd6aa11..a08feca42c 100644 --- a/crates/autopilot/src/database.rs +++ b/crates/autopilot/src/database.rs @@ -6,7 +6,6 @@ use { mod auction; pub mod auction_prices; -pub mod auction_transaction; pub mod competition; pub mod ethflow_events; mod events; diff --git a/crates/autopilot/src/database/auction_transaction.rs b/crates/autopilot/src/database/auction_transaction.rs deleted file mode 100644 index 96cccc3edf..0000000000 --- a/crates/autopilot/src/database/auction_transaction.rs +++ /dev/null @@ -1,50 +0,0 @@ -use { - anyhow::Context, - database::{auction_transaction::SettlementEvent, byte_array::ByteArray}, - primitive_types::H160, -}; - -impl super::Postgres { - pub async fn update_settlement_tx_info( - &self, - block_number: i64, - log_index: i64, - tx_from: H160, - tx_nonce: i64, - ) -> anyhow::Result<()> { - let _timer = super::Metrics::get() - .database_queries - .with_label_values(&["update_settlement_tx_info"]) - .start_timer(); - - let mut ex = self.pool.acquire().await.context("acquire")?; - database::auction_transaction::insert_settlement_tx_info( - &mut ex, - block_number, - log_index, - &ByteArray(tx_from.0), - tx_nonce, - ) - .await - .context("insert_settlement_tx_info")?; - - Ok(()) - } - - pub async fn get_settlement_event_without_tx_info( - &self, - max_block_number: i64, - ) -> Result, sqlx::Error> { - let _timer = super::Metrics::get() - .database_queries - .with_label_values(&["get_settlement_event_without_tx_info"]) - .start_timer(); - - let mut ex = self.pool.acquire().await?; - database::auction_transaction::get_settlement_event_without_tx_info( - &mut ex, - max_block_number, - ) - .await - } -} diff --git a/crates/autopilot/src/database/on_settlement_event_updater.rs b/crates/autopilot/src/database/on_settlement_event_updater.rs index ff1760c179..2fa304be39 100644 --- a/crates/autopilot/src/database/on_settlement_event_updater.rs +++ b/crates/autopilot/src/database/on_settlement_event_updater.rs @@ -1,7 +1,7 @@ use { anyhow::{Context, Result}, database::{byte_array::ByteArray, settlement_observations::Observation}, - ethcontract::{H160, U256}, + ethcontract::U256, model::order::OrderUid, number::conversions::u256_to_big_decimal, sqlx::PgConnection, @@ -13,7 +13,6 @@ pub type AuctionId = i64; #[derive(Debug, Default, Clone)] pub struct AuctionData { - pub auction_id: AuctionId, pub gas_used: U256, pub effective_gas_price: U256, pub surplus: U256, @@ -21,12 +20,12 @@ pub struct AuctionData { pub order_executions: Vec<(OrderUid, ExecutedFee)>, } -#[derive(Debug, Default, Clone)] +#[derive(Debug, Clone)] pub struct SettlementUpdate { pub block_number: i64, pub log_index: i64, - pub tx_from: H160, - pub tx_nonce: i64, + pub auction_id: AuctionId, + /// Only set if the auction is for this environment. pub auction_data: Option, } @@ -41,31 +40,16 @@ impl super::Postgres { .start_timer(); // update settlements - database::auction_transaction::insert_settlement_tx_info( + database::settlements::update_settlement_auction( ex, settlement_update.block_number, settlement_update.log_index, - &ByteArray(settlement_update.tx_from.0), - settlement_update.tx_nonce, + settlement_update.auction_id, ) .await .context("insert_settlement_tx_info")?; if let Some(auction_data) = settlement_update.auction_data { - // Link the `auction_id` to the settlement tx. This is needed for - // colocated solutions and is a no-op for centralized - // solutions. - let insert_succesful = database::auction_transaction::try_insert_auction_transaction( - ex, - auction_data.auction_id, - &ByteArray(settlement_update.tx_from.0), - settlement_update.tx_nonce, - ) - .await - .context("failed to insert auction_transaction")?; - - // in case of deep reindexing we might already have the observation, so just - // overwrite it database::settlement_observations::upsert( ex, Observation { @@ -80,17 +64,15 @@ impl super::Postgres { .await .context("insert_settlement_observations")?; - if insert_succesful { - for (order, executed_fee) in auction_data.order_executions { - database::order_execution::save( - ex, - &ByteArray(order.0), - auction_data.auction_id, - &u256_to_big_decimal(&executed_fee), - ) - .await - .context("save_order_executions")?; - } + for (order, executed_fee) in auction_data.order_executions { + database::order_execution::save( + ex, + &ByteArray(order.0), + settlement_update.auction_id, + &u256_to_big_decimal(&executed_fee), + ) + .await + .context("save_order_executions")?; } } Ok(()) diff --git a/crates/autopilot/src/on_settlement_event_updater.rs b/crates/autopilot/src/on_settlement_event_updater.rs index 202e0a6597..ccdccc978c 100644 --- a/crates/autopilot/src/on_settlement_event_updater.rs +++ b/crates/autopilot/src/on_settlement_event_updater.rs @@ -36,10 +36,10 @@ use { on_settlement_event_updater::{AuctionData, SettlementUpdate}, Postgres, }, - decoded_settlement::{DecodedSettlement, DecodingError}, + decoded_settlement::DecodedSettlement, infra, }, - anyhow::{anyhow, Context, Result}, + anyhow::{Context, Result}, futures::StreamExt, primitive_types::H256, shared::{event_handling::MAX_REORG_BLOCK_COUNT, external_prices::ExternalPrices}, @@ -52,6 +52,15 @@ pub struct OnSettlementEventUpdater { pub db: Postgres, } +enum AuctionIdRecoveryStatus { + /// The auction id was recovered and the auction data should be added. + AddAuctionData(i64, DecodedSettlement), + /// The auction id was recovered but the auction data should not be added. + DoNotAddAuctionData(i64), + /// The auction id was not recovered. + InvalidCalldata, +} + impl OnSettlementEventUpdater { pub async fn run_forever(self) -> ! { let mut current_block = self.eth.current_block().borrow().to_owned(); @@ -96,16 +105,14 @@ impl OnSettlementEventUpdater { .begin() .await .context("acquire DB connection")?; - let event = match database::auction_transaction::get_settlement_event_without_tx_info( - &mut ex, - reorg_safe_block, - ) - .await - .context("get_settlement_event_without_tx_info")? - { - Some(event) => event, - None => return Ok(false), - }; + let event = + match database::settlements::get_settlement_without_auction(&mut ex, reorg_safe_block) + .await + .context("get_settlement_event_without_tx_info")? + { + Some(event) => event, + None => return Ok(false), + }; let hash = H256(event.tx_hash.0); tracing::debug!("updating settlement details for tx {hash:?}"); @@ -115,111 +122,29 @@ impl OnSettlementEventUpdater { .transaction(hash) .await? .with_context(|| format!("no tx {hash:?}"))?; - let tx_from = transaction - .from - .with_context(|| format!("no from {hash:?}"))?; - let tx_nonce: i64 = transaction - .nonce - .try_into() - .map_err(|err| anyhow!("{}", err)) - .with_context(|| format!("convert nonce {hash:?}"))?; - let auction_id = Self::recover_auction_id_from_calldata(&mut ex, &transaction).await?; + let (auction_id, auction_data) = + match Self::recover_auction_id_from_calldata(&mut ex, &transaction).await? { + AuctionIdRecoveryStatus::InvalidCalldata => { + return Ok(false); + } + AuctionIdRecoveryStatus::DoNotAddAuctionData(auction_id) => (auction_id, None), + AuctionIdRecoveryStatus::AddAuctionData(auction_id, settlement) => ( + auction_id, + Some( + self.fetch_auction_data(hash, settlement, auction_id, &mut ex) + .await?, + ), + ), + }; - let mut update = SettlementUpdate { + let update = SettlementUpdate { block_number: event.block_number, log_index: event.log_index, - tx_from, - tx_nonce, - auction_data: None, + auction_id, + auction_data, }; - // It is possible that auction_id does not exist for a transaction. - // This happens for production auctions queried from the staging environment and - // vice versa (because we have databases for both environments). - // - // If auction_id exists, we expect all other relevant information to exist as - // well. - if let Some(auction_id) = auction_id { - let receipt = self - .eth - .transaction_receipt(hash) - .await? - .with_context(|| format!("no receipt {hash:?}"))?; - let gas_used = receipt - .gas_used - .with_context(|| format!("no gas used {hash:?}"))?; - let effective_gas_price = receipt - .effective_gas_price - .with_context(|| format!("no effective gas price {hash:?}"))?; - let auction_external_prices = Postgres::get_auction_prices(&mut ex, auction_id) - .await - .with_context(|| { - format!("no external prices for auction id {auction_id:?} and tx {hash:?}") - })?; - let external_prices = ExternalPrices::try_from_auction_prices( - self.eth.contracts().weth().address(), - auction_external_prices.clone(), - )?; - - tracing::debug!( - ?auction_id, - ?auction_external_prices, - ?external_prices, - "observations input" - ); - - // surplus and fees calculation - match DecodedSettlement::new(&transaction.input.0) { - Ok(settlement) => { - let domain_separator = self.eth.contracts().settlement_domain_separator(); - let order_uids = settlement.order_uids(domain_separator)?; - let order_fees = order_uids - .clone() - .into_iter() - .zip(Postgres::order_fees(&mut ex, &order_uids).await?) - .collect::>(); - - let surplus = settlement.total_surplus(&external_prices); - let (fee, order_executions) = { - let all_fees = settlement.all_fees(&external_prices, &order_fees); - // total unsubsidized fee used for CIP20 rewards - let fee = all_fees - .iter() - .fold(0.into(), |acc, fees| acc + fees.native); - // executed fees for each order execution - let order_executions = all_fees - .into_iter() - .zip(order_fees.iter()) - .map(|(fee, (_, order_fee))| match order_fee { - // market orders have no surplus fee - Some(_) => (fee.order, 0.into()), - None => (fee.order, fee.sell), - }) - .collect(); - (fee, order_executions) - }; - - update.auction_data = Some(AuctionData { - auction_id, - surplus, - fee, - gas_used, - effective_gas_price, - order_executions, - }); - } - Err(DecodingError::InvalidSelector) => { - // we indexed a transaction initiated by solver, that was not a settlement - // for this case we want to have the entry in observations table but with zeros - update.auction_data = Some(Default::default()); - } - Err(err) => { - return Err(err.into()); - } - } - } - tracing::debug!(?hash, ?update, "updating settlement details for tx"); Postgres::update_settlement_details(&mut ex, update.clone()) @@ -229,45 +154,120 @@ impl OnSettlementEventUpdater { Ok(true) } + async fn fetch_auction_data( + &self, + hash: H256, + settlement: DecodedSettlement, + auction_id: i64, + ex: &mut PgConnection, + ) -> Result { + let receipt = self + .eth + .transaction_receipt(hash) + .await? + .with_context(|| format!("no receipt {hash:?}"))?; + let gas_used = receipt + .gas_used + .with_context(|| format!("no gas used {hash:?}"))?; + let effective_gas_price = receipt + .effective_gas_price + .with_context(|| format!("no effective gas price {hash:?}"))?; + let auction_external_prices = Postgres::get_auction_prices(ex, auction_id) + .await + .with_context(|| { + format!("no external prices for auction id {auction_id:?} and tx {hash:?}") + })?; + let external_prices = ExternalPrices::try_from_auction_prices( + self.eth.contracts().weth().address(), + auction_external_prices.clone(), + )?; + + tracing::debug!( + ?auction_id, + ?auction_external_prices, + ?external_prices, + "observations input" + ); + + // surplus and fees calculation + let domain_separator = self.eth.contracts().settlement_domain_separator(); + let order_uids = settlement.order_uids(domain_separator)?; + let order_fees = order_uids + .clone() + .into_iter() + .zip(Postgres::order_fees(ex, &order_uids).await?) + .collect::>(); + + let surplus = settlement.total_surplus(&external_prices); + let (fee, order_executions) = { + let all_fees = settlement.all_fees(&external_prices, &order_fees); + // total unsubsidized fee used for CIP20 rewards + let fee = all_fees + .iter() + .fold(0.into(), |acc, fees| acc + fees.native); + // executed fees for each order execution + let order_executions = all_fees + .into_iter() + .zip(order_fees.iter()) + .map(|(fee, (_, order_fee))| match order_fee { + // market orders have no surplus fee + Some(_) => (fee.order, 0.into()), + None => (fee.order, fee.sell), + }) + .collect(); + (fee, order_executions) + }; + + Ok(AuctionData { + surplus, + fee, + gas_used, + effective_gas_price, + order_executions, + }) + } + /// With solver driver colocation solvers are supposed to append the /// `auction_id` to the settlement calldata. This function tries to - /// recover that `auction_id`. This function only returns an error if - /// retrying the operation makes sense. If all went well and there - /// simply is no sensible `auction_id` `Ok(None)` will be returned. + /// recover that `auction_id`. It also indicates whether the auction + /// should be indexed with its metadata. (ie. if it comes from this + /// environment and not from a different instance of the autopilot, e.g. + /// running in barn/prod). This function only returns an error + /// if retrying the operation makes sense. async fn recover_auction_id_from_calldata( ex: &mut PgConnection, tx: &Transaction, - ) -> Result> { + ) -> Result { let tx_from = tx.from.context("tx is missing sender")?; - let metadata = match DecodedSettlement::new(&tx.input.0) { - Ok(settlement) => settlement.metadata, + let settlement = match DecodedSettlement::new(&tx.input.0) { + Ok(settlement) => settlement, Err(err) => { tracing::warn!( ?tx, ?err, "could not decode settlement tx, unclear which auction it belongs to" ); - return Ok(None); + return Ok(AuctionIdRecoveryStatus::InvalidCalldata); } }; - let auction_id = match metadata { + let auction_id = match settlement.metadata { Some(bytes) => i64::from_be_bytes(bytes.0), None => { tracing::warn!(?tx, "could not recover the auction_id from the calldata"); - return Ok(None); + return Ok(AuctionIdRecoveryStatus::InvalidCalldata); } }; let score = database::settlement_scores::fetch(ex, auction_id).await?; let data_already_recorded = - database::auction_transaction::data_exists(ex, auction_id).await?; + database::settlements::already_processed(ex, auction_id).await?; match (score, data_already_recorded) { (None, _) => { tracing::debug!( auction_id, "calldata claims to settle auction that has no competition" ); - Ok(None) + Ok(AuctionIdRecoveryStatus::DoNotAddAuctionData(auction_id)) } (Some(score), _) if score.winner.0 != tx_from.0 => { tracing::warn!( @@ -276,16 +276,18 @@ impl OnSettlementEventUpdater { winner = ?score.winner, "solution submitted by solver other than the winner" ); - Ok(None) + Ok(AuctionIdRecoveryStatus::DoNotAddAuctionData(auction_id)) } (Some(_), true) => { tracing::warn!( auction_id, "settlement data already recorded for this auction" ); - Ok(None) + Ok(AuctionIdRecoveryStatus::DoNotAddAuctionData(auction_id)) } - (Some(_), false) => Ok(Some(auction_id)), + (Some(_), false) => Ok(AuctionIdRecoveryStatus::AddAuctionData( + auction_id, settlement, + )), } } } diff --git a/crates/database/src/auction_transaction.rs b/crates/database/src/auction_transaction.rs deleted file mode 100644 index 5555cf66dd..0000000000 --- a/crates/database/src/auction_transaction.rs +++ /dev/null @@ -1,205 +0,0 @@ -use { - crate::{auction::AuctionId, Address, TransactionHash}, - sqlx::{postgres::PgQueryResult, PgConnection}, -}; - -/// Inserts a row **iff** we don't have an entry for the given `auction_id` yet. -/// This is useful to associate a settlement transaction coming from a colocated -/// driver with an auction. -/// In that case anybody could claim to settle the given auction but we only -/// ever want to store the first claim. -pub async fn try_insert_auction_transaction( - ex: &mut PgConnection, - auction_id: AuctionId, - tx_from: &Address, - tx_nonce: i64, -) -> Result { - const QUERY: &str = r#" - INSERT INTO auction_transaction (auction_id, tx_from, tx_nonce) - VALUES ($1, $2, $3) - ON CONFLICT (auction_id) DO NOTHING - "#; - - let result = sqlx::query(QUERY) - .bind(auction_id) - .bind(tx_from) - .bind(tx_nonce) - .execute(ex) - .await?; - - Ok(result.rows_affected() == 1) -} - -pub async fn insert_settlement_tx_info( - ex: &mut PgConnection, - block_number: i64, - log_index: i64, - tx_from: &Address, - tx_nonce: i64, -) -> Result { - const QUERY: &str = r#" -UPDATE settlements -SET tx_from = $1, tx_nonce = $2 -WHERE block_number = $3 AND log_index = $4 - ;"#; - sqlx::query(QUERY) - .bind(tx_from) - .bind(tx_nonce) - .bind(block_number) - .bind(log_index) - .execute(ex) - .await -} - -#[derive(Debug, sqlx::FromRow)] -pub struct SettlementEvent { - pub block_number: i64, - pub log_index: i64, - pub tx_hash: TransactionHash, -} - -pub async fn get_settlement_event_without_tx_info( - ex: &mut PgConnection, - max_block_number: i64, -) -> Result, sqlx::Error> { - const QUERY: &str = r#" -SELECT block_number, log_index, tx_hash -FROM settlements -WHERE tx_from IS NULL AND block_number <= $1 -LIMIT 1 - "#; - sqlx::query_as(QUERY) - .bind(max_block_number) - .fetch_optional(ex) - .await -} - -pub async fn data_exists(ex: &mut PgConnection, auction_id: i64) -> Result { - const QUERY: &str = r#"SELECT COUNT(*) FROM auction_transaction WHERE auction_id = $1;"#; - let count: i64 = sqlx::query_scalar(QUERY) - .bind(auction_id) - .fetch_one(ex) - .await?; - Ok(count >= 1) -} - -#[cfg(test)] -mod tests { - use { - super::*, - crate::events::{Event, EventIndex, Settlement}, - sqlx::Connection, - std::ops::DerefMut, - }; - - #[tokio::test] - #[ignore] - async fn insert_settlement_tx_info_() { - let mut db = PgConnection::connect("postgresql://").await.unwrap(); - let mut db = db.begin().await.unwrap(); - crate::clear_DANGER_(&mut db).await.unwrap(); - - let index = EventIndex::default(); - let event = Event::Settlement(Settlement { - solver: Default::default(), - transaction_hash: Default::default(), - }); - crate::events::append(&mut db, &[(index, event)]) - .await - .unwrap(); - - let auction_id: Option = sqlx::query_scalar("SELECT tx_nonce FROM settlements") - .fetch_one(db.deref_mut()) - .await - .unwrap(); - assert_eq!(auction_id, None); - - insert_settlement_tx_info(&mut db, 0, 0, &Default::default(), 1) - .await - .unwrap(); - - let auction_id: Option = sqlx::query_scalar("SELECT tx_nonce FROM settlements") - .fetch_one(db.deref_mut()) - .await - .unwrap(); - assert_eq!(auction_id, Some(1)); - } - - #[tokio::test] - #[ignore] - async fn get_settlement_event_without_tx_info_() { - let mut db = PgConnection::connect("postgresql://").await.unwrap(); - let mut db = db.begin().await.unwrap(); - crate::clear_DANGER_(&mut db).await.unwrap(); - - let event = get_settlement_event_without_tx_info(&mut db, 10) - .await - .unwrap(); - assert!(event.is_none()); - - // event at block 0 - let index = EventIndex::default(); - let event = Event::Settlement(Settlement { - solver: Default::default(), - transaction_hash: Default::default(), - }); - crate::events::append(&mut db, &[(index, event)]) - .await - .unwrap(); - - // is found - let event = get_settlement_event_without_tx_info(&mut db, 10) - .await - .unwrap() - .unwrap(); - assert_eq!(event.block_number, 0); - - // gets tx info - insert_settlement_tx_info(&mut db, 0, 0, &Default::default(), 1) - .await - .unwrap(); - - // no longer found - let event = get_settlement_event_without_tx_info(&mut db, 10) - .await - .unwrap(); - assert!(event.is_none()); - - // event at 11 - let index = EventIndex { - block_number: 11, - log_index: 0, - }; - let event = Event::Settlement(Settlement { - solver: Default::default(), - transaction_hash: Default::default(), - }); - crate::events::append(&mut db, &[(index, event)]) - .await - .unwrap(); - - // not found because block number too large - let event = get_settlement_event_without_tx_info(&mut db, 10) - .await - .unwrap(); - assert!(event.is_none()); - } - - #[tokio::test] - #[ignore] - async fn try_insert_auction_transaction_test() { - let mut db = PgConnection::connect("postgresql://").await.unwrap(); - let mut db = db.begin().await.unwrap(); - crate::clear_DANGER_(&mut db).await.unwrap(); - - let inserted = try_insert_auction_transaction(&mut db, 3, &Default::default(), 1) - .await - .unwrap(); - assert!(inserted); - - let inserted = try_insert_auction_transaction(&mut db, 3, &Default::default(), 1) - .await - .unwrap(); - assert!(!inserted); - } -} diff --git a/crates/database/src/lib.rs b/crates/database/src/lib.rs index 77046a684e..016bded60d 100644 --- a/crates/database/src/lib.rs +++ b/crates/database/src/lib.rs @@ -2,7 +2,6 @@ pub mod app_data; pub mod auction; pub mod auction_participants; pub mod auction_prices; -pub mod auction_transaction; pub mod byte_array; pub mod ethflow_orders; pub mod events; @@ -59,7 +58,6 @@ pub const TABLES: &[&str] = &[ "ethflow_orders", "order_execution", "interactions", - "auction_transaction", "ethflow_refunds", "settlement_scores", "settlement_observations", diff --git a/crates/database/src/settlements.rs b/crates/database/src/settlements.rs index c8cc07fa50..409ce64fa5 100644 --- a/crates/database/src/settlements.rs +++ b/crates/database/src/settlements.rs @@ -40,6 +40,63 @@ WHERE .await } +#[derive(Debug, sqlx::FromRow)] +pub struct SettlementEvent { + pub block_number: i64, + pub log_index: i64, + pub tx_hash: TransactionHash, +} + +pub async fn get_settlement_without_auction( + ex: &mut PgConnection, + max_block_number: i64, +) -> Result, sqlx::Error> { + const QUERY: &str = r#" +SELECT block_number, log_index, tx_hash +FROM settlements +WHERE auction_id IS NULL +AND block_number <= $1 +ORDER BY block_number ASC +LIMIT 1 + "#; + sqlx::query_as(QUERY) + .bind(max_block_number) + .fetch_optional(ex) + .await +} + +pub async fn already_processed( + ex: &mut PgConnection, + auction_id: i64, +) -> Result { + const QUERY: &str = r#"SELECT COUNT(*) FROM settlements WHERE auction_id = $1;"#; + let count: i64 = sqlx::query_scalar(QUERY) + .bind(auction_id) + .fetch_one(ex) + .await?; + Ok(count >= 1) +} + +pub async fn update_settlement_auction( + ex: &mut PgConnection, + block_number: i64, + log_index: i64, + auction_id: i64, +) -> Result<(), sqlx::Error> { + const QUERY: &str = r#" +UPDATE settlements +SET auction_id = $1 +WHERE block_number = $2 AND log_index = $3 + ;"#; + sqlx::query(QUERY) + .bind(auction_id) + .bind(block_number) + .bind(log_index) + .execute(ex) + .await + .map(|_| ()) +} + #[cfg(test)] mod tests { use { @@ -108,4 +165,33 @@ mod tests { let results = recent_settlement_tx_hashes(&mut db, 3..5).await.unwrap(); assert_eq!(results, &[]); } + + #[tokio::test] + #[ignore] + async fn postgres_settlement_auction() { + let mut db = PgConnection::connect("postgresql://").await.unwrap(); + let mut db = db.begin().await.unwrap(); + crate::clear_DANGER_(&mut db).await.unwrap(); + + let event = Default::default(); + crate::events::insert_settlement(&mut db, &event, &Default::default()) + .await + .unwrap(); + + let settlement = get_settlement_without_auction(&mut db, 0) + .await + .unwrap() + .unwrap(); + + assert_eq!(settlement.block_number, event.block_number); + assert_eq!(settlement.log_index, event.log_index); + + update_settlement_auction(&mut db, event.block_number, event.log_index, 1) + .await + .unwrap(); + + let settlement = get_settlement_without_auction(&mut db, 0).await.unwrap(); + + assert!(settlement.is_none()); + } } diff --git a/crates/database/src/solver_competition.rs b/crates/database/src/solver_competition.rs index 4c2ee98fc4..def53d0bde 100644 --- a/crates/database/src/solver_competition.rs +++ b/crates/database/src/solver_competition.rs @@ -31,8 +31,7 @@ pub async fn load_by_id( SELECT sc.json, sc.id, s.tx_hash FROM solver_competitions sc -- outer joins because the data might not have been indexed yet -LEFT OUTER JOIN auction_transaction at ON sc.id = at.auction_id -LEFT OUTER JOIN settlements s ON (at.tx_from, at.tx_nonce) = (s.tx_from, s.tx_nonce) +LEFT OUTER JOIN settlements s ON sc.id = s.auction_id WHERE sc.id = $1 ;"#; sqlx::query_as(QUERY).bind(id).fetch_optional(ex).await @@ -45,8 +44,7 @@ pub async fn load_latest_competition( SELECT sc.json, sc.id, s.tx_hash FROM solver_competitions sc -- outer joins because the data might not have been indexed yet -LEFT OUTER JOIN auction_transaction at ON sc.id = at.auction_id -LEFT OUTER JOIN settlements s ON (at.tx_from, at.tx_nonce) = (s.tx_from, s.tx_nonce) +LEFT OUTER JOIN settlements s ON sc.id = s.auction_id ORDER BY sc.id DESC LIMIT 1 ;"#; @@ -60,8 +58,7 @@ pub async fn load_by_tx_hash( const QUERY: &str = r#" SELECT sc.json, sc.id, s.tx_hash FROM solver_competitions sc -JOIN auction_transaction at ON sc.id = at.auction_id -JOIN settlements s ON (at.tx_from, at.tx_nonce) = (s.tx_from, s.tx_nonce) +JOIN settlements s ON sc.id = s.auction_id WHERE s.tx_hash = $1 ;"#; sqlx::query_as(QUERY).bind(tx_hash).fetch_optional(ex).await @@ -74,7 +71,6 @@ mod tests { crate::{ byte_array::ByteArray, events::{Event, EventIndex, Settlement}, - Address, }, sqlx::Connection, }; @@ -127,22 +123,15 @@ mod tests { .await .unwrap(); - let tx_from: Address = ByteArray([0x01; 20]); - let tx_nonce: i64 = 2; - crate::auction_transaction::insert_settlement_tx_info( + crate::settlements::update_settlement_auction( &mut db, index.block_number, index.log_index, - &tx_from, - tx_nonce, + id, ) .await .unwrap(); - crate::auction_transaction::try_insert_auction_transaction(&mut db, id, &tx_from, tx_nonce) - .await - .unwrap(); - // Now succeeds. let value_by_hash = load_by_tx_hash(&mut db, &hash).await.unwrap().unwrap(); assert_eq!(value, value_by_hash.json); diff --git a/crates/e2e/tests/e2e/database.rs b/crates/e2e/tests/e2e/database.rs index d18364e146..58a89c00ec 100644 --- a/crates/e2e/tests/e2e/database.rs +++ b/crates/e2e/tests/e2e/database.rs @@ -21,8 +21,6 @@ pub async fn events_of_order(db: &Db, uid: &OrderUid) -> Vec Option { let mut db = db.acquire().await.unwrap(); - const LAST_AUCTION_ID: &str = - "SELECT auction_id FROM auction_transaction ORDER BY auction_id DESC LIMIT 1"; + const LAST_AUCTION_ID: &str = "SELECT auction_id FROM settlements WHERE auction_id IS NOT \ + NULL ORDER BY auction_id DESC LIMIT 1"; let auction_id: i64 = sqlx::query_scalar(LAST_AUCTION_ID) .fetch_optional(db.deref_mut()) .await .unwrap()?; const TX_QUERY: &str = r" -SELECT s.* -FROM auction_transaction at -JOIN settlements s ON s.tx_from = at.tx_from AND s.tx_nonce = at.tx_nonce -WHERE at.auction_id = $1 - "; +SELECT * FROM settlements WHERE auction_id = $1"; + let tx: AuctionTransaction = sqlx::query_as(TX_QUERY) .bind(auction_id) .fetch_optional(db.deref_mut()) diff --git a/crates/e2e/tests/e2e/univ2.rs b/crates/e2e/tests/e2e/univ2.rs index c4d31ef8e4..6718e5d292 100644 --- a/crates/e2e/tests/e2e/univ2.rs +++ b/crates/e2e/tests/e2e/univ2.rs @@ -105,8 +105,6 @@ async fn test(web3: Web3) { && data.participants.iter().any(|p| p.participant.0 == solver.address().0) // and won the auction && data.score.winner.0 == solver.address().0 - // and submitted the solution - && data.tx.tx_from.0 == solver.address().0 // and calldata is present && !data.call_data.call_data.is_empty() }; diff --git a/crates/orderbook/src/database/solver_competition.rs b/crates/orderbook/src/database/solver_competition.rs index ababfc419c..cfabb1065c 100644 --- a/crates/orderbook/src/database/solver_competition.rs +++ b/crates/orderbook/src/database/solver_competition.rs @@ -48,8 +48,6 @@ impl SolverCompetitionStoring for Postgres { row.tx_hash.map(|hash| H256(hash.0)), ) }), - // TODO: change this query to use the auction_transaction and settlements tables to - // find the tx hash. Identifier::Transaction(hash) => { database::solver_competition::load_by_tx_hash(&mut ex, &ByteArray(hash.0)) .await diff --git a/database/sql/V058__add_auction_id_to_settlements.sql b/database/sql/V058__add_auction_id_to_settlements.sql new file mode 100644 index 0000000000..2278aa1d28 --- /dev/null +++ b/database/sql/V058__add_auction_id_to_settlements.sql @@ -0,0 +1,19 @@ +-- Step 1: Add the new column to the settlements table +ALTER TABLE settlements + ADD COLUMN auction_id bigint; + +-- Step 2: Populate auction_id columns + +-- For all settlements that are not potentially still waiting to get indexed, set the auction ID to a default value of 0 +-- Using 1000 blocks here to leave some margin for possible settlement updates that are still inflight. +UPDATE settlements +SET auction_id = 0 +WHERE block_number < (SELECT max(block_number) FROM settlements) - 1000; + +-- For all settlements that already have an auction_transaction record, set auction_id to the auction_transaction's auction_id +UPDATE settlements +SET auction_id = auction_transaction.auction_id +FROM auction_transaction +WHERE settlements.tx_from = auction_transaction.tx_from AND settlements.tx_nonce = auction_transaction.tx_nonce; + +-- Step 3: (Once migration has been successful) Drop the auction_transaction table, and the tx_from and tx_nonce columns from the settlements table