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

edits_feb5 #15

Merged
merged 11 commits into from
Feb 13, 2024
Merged
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
71 changes: 51 additions & 20 deletions auction-server/src/api/liquidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ pub struct OpportunityParamsWithId {
params: OpportunityParams,
}

impl Into<OpportunityParamsWithId> for LiquidationOpportunity {
fn into(self) -> OpportunityParamsWithId {
OpportunityParamsWithId {
opportunity_id: self.id,
params: self.params,
}
}
}

/// Submit a liquidation opportunity ready to be executed.
///
/// The opportunity will be verified by the server. If the opportunity is valid, it will be stored in the database
Expand Down Expand Up @@ -105,12 +114,29 @@ pub async fn post_opportunity(
.await
.map_err(|e| RestError::InvalidOpportunity(e.to_string()))?;

store
.liquidation_store
.opportunities
.write()
.await
.insert(params.permission_key.clone(), opportunity);

let mut write_lock = store.liquidation_store.opportunities.write().await;

if let Some(opportunities_existing) = write_lock.get_mut(&params.permission_key) {
// check if same opportunity exists in the vector
for opportunity_existing in opportunities_existing.clone() {
if opportunity_existing == opportunity {
return Err(RestError::BadParameters(
"Duplicate opportunity submission".to_string(),
));
}
}

opportunities_existing.push(opportunity);
} else {
write_lock.insert(params.permission_key.clone(), vec![opportunity]);
}

tracing::debug!("number of permission keys: {}", write_lock.len());
tracing::debug!(
"number of opportunities for key: {}",
write_lock[&params.permission_key].len()
);

Ok(OpportunityParamsWithId {
opportunity_id: id,
Expand Down Expand Up @@ -144,11 +170,14 @@ pub async fn get_opportunities(
.await
.values()
.cloned()
.map(|opportunity| OpportunityParamsWithId {
opportunity_id: opportunity.id,
params: opportunity.params,
.map(|opportunities_key| {
opportunities_key
.last()
.expect("A permission key vector should have at least one opportunity")
.clone()
.into()
})
.filter(|params_with_id| {
.filter(|params_with_id: &OpportunityParamsWithId| {
let params = match &params_with_id.params {
OpportunityParams::V1(params) => params,
};
Expand Down Expand Up @@ -199,7 +228,7 @@ pub async fn post_bid(
Path(opportunity_id): Path<Uuid>,
Json(opportunity_bid): Json<OpportunityBid>,
) -> Result<Json<BidResult>, RestError> {
let opportunity = store
let opportunities = store
.liquidation_store
.opportunities
.read()
Expand All @@ -208,12 +237,10 @@ pub async fn post_bid(
.ok_or(RestError::OpportunityNotFound)?
.clone();


if opportunity.id != opportunity_id {
return Err(RestError::BadParameters(
"Invalid opportunity_id".to_string(),
));
}
let opportunity = opportunities
.iter()
.find(|o| o.id == opportunity_id)
.ok_or(RestError::OpportunityNotFound)?;

// TODO: move this logic to searcher side
if opportunity.bidders.contains(&opportunity_bid.liquidator) {
Expand Down Expand Up @@ -253,9 +280,13 @@ pub async fn post_bid(
{
Ok(_) => {
let mut write_guard = store.liquidation_store.opportunities.write().await;
let liquidation = write_guard.get_mut(&opportunity_bid.permission_key);
if let Some(liquidation) = liquidation {
liquidation.bidders.insert(opportunity_bid.liquidator);
let opportunities = write_guard.get_mut(&opportunity_bid.permission_key);
if let Some(opportunities) = opportunities {
let opportunity = opportunities
.iter_mut()
.find(|o| o.id == opportunity_id)
.ok_or(RestError::OpportunityNotFound)?;
opportunity.bidders.insert(opportunity_bid.liquidator);
}
Ok(BidResult {
status: "OK".to_string(),
Expand Down
41 changes: 29 additions & 12 deletions auction-server/src/liquidation_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,25 +333,42 @@ async fn verify_with_store(opportunity: LiquidationOpportunity, store: &Store) -
/// # Arguments
///
/// * `store`: server store
pub async fn run_verification_loop(store: Arc<Store>) {
pub async fn run_verification_loop(store: Arc<Store>) -> Result<()> {
tracing::info!("Starting opportunity verifier...");
while !SHOULD_EXIT.load(Ordering::Acquire) {
let all_opportunities = store.liquidation_store.opportunities.read().await.clone();
for (permission_key, opportunity) in all_opportunities.iter() {
match verify_with_store(opportunity.clone(), &store).await {
Ok(_) => {}
Err(e) => {
store
.liquidation_store
.opportunities
.write()
.await
.remove(permission_key);
tracing::info!("Removed Opportunity with failed verification: {}", e);
for (permission_key, opportunities) in all_opportunities.iter() {
// check each of the opportunities for this permission key for validity
let mut opps_to_remove = vec![];
for opportunity in opportunities.iter() {
match verify_with_store(opportunity.clone(), &store).await {
Ok(_) => {}
Err(e) => {
opps_to_remove.push(opportunity.id);
tracing::info!(
"Removing Opportunity {} with failed verification: {}",
opportunity.id,
e
);
}
}
}

// set write lock to remove all these opportunities
let mut write_lock = store.liquidation_store.opportunities.write().await;

if let Some(opportunities) = write_lock.get_mut(permission_key) {
opportunities.retain(|x| !opps_to_remove.contains(&x.id));
if opportunities.is_empty() {
write_lock.remove(permission_key);
}
}

// release the write lock
drop(write_lock);
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we need to explicitly drop it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is good to explicitly ensure that the write lock is dropped and other processes can access the object here. otherwise, the write lock could persist through other loops of rpc calls

}
tokio::time::sleep(Duration::from_secs(5)).await; // this should be replaced by a subscription to the chain and trigger on new blocks
}
tracing::info!("Shutting down opportunity verifier...");
Ok(())
}
10 changes: 5 additions & 5 deletions auction-server/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub struct SimulatedBid {

pub type UnixTimestamp = i64;

#[derive(Serialize, Deserialize, ToSchema, Clone)]
#[derive(Serialize, Deserialize, ToSchema, Clone, PartialEq)]
pub struct TokenQty {
/// Token contract address
#[schema(example = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",value_type=String)]
Expand All @@ -55,7 +55,7 @@ pub struct TokenQty {
/// If a searcher signs the opportunity and have approved enough tokens to liquidation adapter,
/// by calling this contract with the given calldata and structures, they will receive the tokens specified
/// in the receipt_tokens field, and will send the tokens specified in the repay_tokens field.
#[derive(Serialize, Deserialize, ToSchema, Clone)]
#[derive(Serialize, Deserialize, ToSchema, Clone, PartialEq)]
pub struct OpportunityParamsV1 {
/// The permission key required for succesful execution of the liquidation.
#[schema(example = "0xdeadbeefcafe", value_type=String)]
Expand All @@ -78,14 +78,14 @@ pub struct OpportunityParamsV1 {
pub receipt_tokens: Vec<TokenQty>,
}

#[derive(Serialize, Deserialize, ToSchema, Clone)]
#[derive(Serialize, Deserialize, ToSchema, Clone, PartialEq)]
#[serde(tag = "version")]
pub enum OpportunityParams {
#[serde(rename = "v1")]
V1(OpportunityParamsV1),
}

#[derive(Clone)]
#[derive(Clone, PartialEq)]
pub struct LiquidationOpportunity {
pub id: Uuid,
pub creation_time: UnixTimestamp,
Expand All @@ -112,7 +112,7 @@ pub struct ChainStore {

#[derive(Default)]
pub struct LiquidationStore {
pub opportunities: RwLock<HashMap<PermissionKey, LiquidationOpportunity>>,
pub opportunities: RwLock<HashMap<PermissionKey, Vec<LiquidationOpportunity>>>,
}

pub struct Store {
Expand Down
Loading
Loading