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

Settlement executions API #3290

Closed
Closed
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
98 changes: 65 additions & 33 deletions crates/database/src/settlement_executions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ use {
sqlx::PgConnection,
};

#[derive(Debug, Clone, Eq, PartialEq, sqlx::FromRow)]
pub struct ExecutionRow {
pub auction_id: AuctionId,
pub solver: Address,
pub start_timestamp: DateTime<Utc>,
pub end_timestamp: Option<DateTime<Utc>>,
pub start_block: i64,
pub end_block: Option<i64>,
pub deadline_block: i64,
pub outcome: Option<String>,
}

pub async fn insert(
ex: &mut PgConnection,
auction_id: AuctionId,
Expand Down Expand Up @@ -55,6 +67,25 @@ WHERE auction_id = $1 AND solver = $2
Ok(())
}

pub async fn find(
ex: &mut PgConnection,
from_auction_id: AuctionId,
to_auction_id: AuctionId,
) -> Result<Vec<ExecutionRow>, sqlx::Error> {
const QUERY: &str = r#"
SELECT * FROM settlement_executions
WHERE auction_id >= $1 AND auction_id <= $2
;"#;

let rows = sqlx::query_as::<_, ExecutionRow>(QUERY)
.bind(from_auction_id)
.bind(to_auction_id)
.fetch_all(ex)
.await?;

Ok(rows)
}

#[cfg(test)]
mod tests {
use {
Expand All @@ -71,7 +102,8 @@ mod tests {
let mut db = db.begin().await.unwrap();
crate::clear_DANGER_(&mut db).await.unwrap();

let auction_id = 1;
let auction_id_a = 1;
let auction_id_b = 2;
let solver_a = ByteArray([1u8; 20]);
let solver_b = ByteArray([2u8; 20]);
let start_timestamp = now_truncated_to_microseconds();
Expand All @@ -80,7 +112,7 @@ mod tests {

insert(
&mut db,
auction_id,
auction_id_a,
solver_a,
start_timestamp,
start_block,
Expand All @@ -90,19 +122,29 @@ mod tests {
.unwrap();
insert(
&mut db,
auction_id,
auction_id_a,
solver_b,
start_timestamp,
start_block,
deadline_block,
)
.await
.unwrap();
insert(
&mut db,
auction_id_b,
solver_a,
start_timestamp,
start_block,
deadline_block,
)
.await
.unwrap();

let output = fetch(&mut db, auction_id).await.unwrap();
assert_eq!(output.len(), 2);
let output = find(&mut db, auction_id_a, auction_id_b).await.unwrap();
assert_eq!(output.len(), 3);
let expected_a = ExecutionRow {
auction_id,
auction_id: auction_id_a,
solver: solver_a,
start_timestamp,
end_timestamp: None,
Expand All @@ -112,7 +154,7 @@ mod tests {
outcome: None,
};
let expected_b = ExecutionRow {
auction_id,
auction_id: auction_id_a,
solver: solver_b,
start_timestamp,
end_timestamp: None,
Expand All @@ -121,15 +163,26 @@ mod tests {
deadline_block,
outcome: None,
};
let expected_c = ExecutionRow {
auction_id: auction_id_b,
solver: solver_a,
start_timestamp,
end_timestamp: None,
start_block,
end_block: None,
deadline_block,
outcome: None,
};
assert!(output.contains(&expected_a));
assert!(output.contains(&expected_b));
assert!(output.contains(&expected_c));

let end_timestamp_a = now_truncated_to_microseconds();
let end_block_a = 8;
let outcome_a = "success".to_string();
update(
&mut db,
auction_id,
auction_id_a,
solver_a,
end_timestamp_a,
end_block_a,
Expand All @@ -143,7 +196,7 @@ mod tests {
let outcome_b = "failure".to_string();
update(
&mut db,
auction_id,
auction_id_a,
solver_b,
end_timestamp_b,
end_block_b,
Expand All @@ -152,10 +205,10 @@ mod tests {
.await
.unwrap();

let output = fetch(&mut db, auction_id).await.unwrap();
let output = find(&mut db, auction_id_a, auction_id_a).await.unwrap();
assert_eq!(output.len(), 2);
let expected_a = ExecutionRow {
auction_id,
auction_id: auction_id_a,
solver: solver_a,
start_timestamp,
end_timestamp: Some(end_timestamp_a),
Expand All @@ -165,7 +218,7 @@ mod tests {
outcome: Some(outcome_a),
};
let expected_b = ExecutionRow {
auction_id,
auction_id: auction_id_a,
solver: solver_b,
start_timestamp,
end_timestamp: Some(end_timestamp_b),
Expand All @@ -178,27 +231,6 @@ mod tests {
assert!(output.contains(&expected_b));
}

#[derive(Debug, Clone, Eq, PartialEq, sqlx::FromRow)]
struct ExecutionRow {
pub auction_id: AuctionId,
pub solver: Address,
pub start_timestamp: DateTime<Utc>,
pub end_timestamp: Option<DateTime<Utc>>,
pub start_block: i64,
pub end_block: Option<i64>,
pub deadline_block: i64,
pub outcome: Option<String>,
}

async fn fetch(
ex: &mut PgConnection,
auction_id: AuctionId,
) -> Result<Vec<ExecutionRow>, sqlx::Error> {
const QUERY: &str = r#"SELECT * FROM settlement_executions WHERE auction_id = $1;"#;

sqlx::query_as(QUERY).bind(auction_id).fetch_all(ex).await
}

/// In the DB we use `timestampz` which doesn't store nanoseconds, so we
/// truncate them to make the comparison work.
fn now_truncated_to_microseconds() -> DateTime<Utc> {
Expand Down
7 changes: 6 additions & 1 deletion crates/orderbook/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod get_native_price;
mod get_order_by_uid;
mod get_order_status;
mod get_orders_by_tx;
mod get_settlement_executions;
mod get_solver_competition;
mod get_token_metadata;
mod get_total_surplus;
Expand Down Expand Up @@ -110,7 +111,11 @@ pub fn handle_all_routes(
),
(
"v1/get_token_metadata",
box_filter(get_token_metadata::get_token_metadata(database)),
box_filter(get_token_metadata::get_token_metadata(database.clone())),
),
(
"v1/get_settlement_executions",
box_filter(get_settlement_executions::get(database)),
),
];

Expand Down
47 changes: 47 additions & 0 deletions crates/orderbook/src/api/get_settlement_executions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use {
crate::{database::Postgres, dto::AuctionId},
hyper::StatusCode,
serde::Deserialize,
std::convert::Infallible,
warp::{reply, Filter, Rejection},
};

pub fn get_settlement_executions_request(
) -> impl Filter<Extract = ((AuctionId, AuctionId),), Error = Rejection> + Clone {
warp::path!("v1" / "settlement_executions")
.and(warp::query::<SettlementQuery>())
.and_then(|query: SettlementQuery| async move {
Result::<_, Infallible>::Ok((query.from_auction, query.to_auction))
})
}

#[derive(Debug, Deserialize)]
pub struct SettlementQuery {
pub from_auction: AuctionId,
pub to_auction: AuctionId,
}

pub fn get(db: Postgres) -> impl Filter<Extract = (super::ApiReply,), Error = Rejection> + Clone {
get_settlement_executions_request().and_then(move |(from_auction, to_auction)| {
let db = db.clone();
async move {
let result = db
.find_settlement_executions(from_auction, to_auction)
.await;
let response = match result {
Ok(executions) => reply::with_status(reply::json(&executions), StatusCode::OK),
Err(err) => {
tracing::error!(
?err,
?from_auction,
?to_auction,
"Failed to fetch settlement executions"
);
crate::api::internal_error_reply()
}
};

Result::<_, Infallible>::Ok(response)
}
})
}
45 changes: 43 additions & 2 deletions crates/orderbook/src/database/orders.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use {
super::Postgres,
crate::{dto::TokenMetadata, orderbook::AddOrderError},
crate::{
dto::{AuctionId, SettlementExecution, TokenMetadata},
orderbook::AddOrderError,
},
anyhow::{Context as _, Result},
app_data::AppDataHash,
async_trait::async_trait,
Expand All @@ -27,7 +30,7 @@ use {
signature::Signature,
time::now_in_epoch_seconds,
},
num::Zero,
num::{ToPrimitive, Zero},
number::conversions::{big_decimal_to_big_uint, big_decimal_to_u256, u256_to_big_decimal},
primitive_types::{H160, U256},
shared::{
Expand Down Expand Up @@ -520,6 +523,44 @@ impl Postgres {
})
}

/// Retrieve all settlements executions for the given auction IDs range.
pub async fn find_settlement_executions(
&self,
from_auction: AuctionId,
to_auction: AuctionId,
) -> Result<Vec<SettlementExecution>> {
let _timer = super::Metrics::get()
.database_queries
.with_label_values(&["find_settlement_executions"])
.start_timer();

let mut ex = self.pool.acquire().await?;
let executions = database::settlement_executions::find(&mut ex, from_auction, to_auction)
.await?
.into_iter()
.map(|row| {
Ok(SettlementExecution {
auction_id: row.auction_id,
solver: H160(row.solver.0),
start_timestamp: row.start_timestamp,
end_timestamp: row.end_timestamp,
start_block: row.start_block.to_u32().context("start block is not u32")?,
end_block: row
.end_block
.map(|block| block.to_u32().context("end block is not u32"))
.transpose()?,
deadline_block: row
.deadline_block
.to_u32()
.context("deadline block is not u32")?,
outcome: row.outcome,
})
})
.collect::<Result<Vec<_>>>()?;

Ok(executions)
}

async fn execute_instrumented<F, T>(&self, label: &str, f: F) -> Result<T>
where
F: std::future::Future<Output = Result<T>>,
Expand Down
17 changes: 16 additions & 1 deletion crates/orderbook/src/dto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ pub use {
order::Order,
};
use {
chrono::{DateTime, Utc},
number::serialization::HexOrDecimalU256,
primitive_types::U256,
primitive_types::{H160, U256},
serde::Serialize,
serde_with::serde_as,
};
Expand All @@ -20,3 +21,17 @@ pub struct TokenMetadata {
#[serde_as(as = "Option<HexOrDecimalU256>")]
pub native_price: Option<U256>,
}

#[serde_as]
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SettlementExecution {
pub auction_id: AuctionId,
pub solver: H160,
pub start_timestamp: DateTime<Utc>,
pub end_timestamp: Option<DateTime<Utc>>,
pub start_block: u32,
pub end_block: Option<u32>,
pub deadline_block: u32,
pub outcome: Option<String>,
}
Loading