Skip to content

Commit

Permalink
Merge pull request #2339 from get10101/fix/prevent-order-submission-i…
Browse files Browse the repository at this point in the history
…n-intermediate-state

chore: Prevent order submission in intermediate dlc channel state
  • Loading branch information
holzeis authored Apr 2, 2024
2 parents 0dd2c09 + 0be2836 commit 71824f6
Showing 1 changed file with 73 additions and 18 deletions.
91 changes: 73 additions & 18 deletions mobile/native/src/trade/order/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::db::get_order_in_filling;
use crate::db::maybe_get_open_orders;
use crate::event;
use crate::event::EventInternal;
use crate::ln_dlc;
use crate::ln_dlc::is_dlc_channel_confirmed;
use crate::trade::order::orderbook_client::OrderbookClient;
use crate::trade::order::FailureReason;
Expand All @@ -19,6 +20,9 @@ use anyhow::Context;
use anyhow::Result;
use commons::ChannelOpeningParams;
use commons::FilledWith;
use dlc_manager::channel::signed_channel::SignedChannel;
use dlc_manager::channel::signed_channel::SignedChannelState;
use ln_dlc_node::node::signed_channel_state_name;
use reqwest::Url;
use rust_decimal::prelude::ToPrimitive;
use time::Duration;
Expand All @@ -38,6 +42,13 @@ pub enum SubmitOrderError {
current_confirmations: u64,
required_confirmations: u64,
},
#[error("DLC Channel in invalid state: expected {expected_channel_state}, got {actual_channel_state}")]
InvalidChannelState {
expected_channel_state: String,
actual_channel_state: String,
},
#[error("Missing DLC channel: {0}")]
MissingChannel(String),
#[error(
"Another order is already being filled: {contracts} contracts {direction} at {leverage}x leverage"
)]
Expand All @@ -54,24 +65,7 @@ pub async fn submit_order(
order: Order,
channel_opening_params: Option<ChannelOpeningParams>,
) -> Result<Uuid, SubmitOrderError> {
// If we have an open position, we should not allow any further trading until the current DLC
// channel is confirmed on-chain. Otherwise we can run into pesky DLC protocol failures.
if position::handler::get_positions()
.map_err(SubmitOrderError::Storage)?
.first()
.is_some()
{
// TODO: We could also limit order submission if we find that the DLC channel is in an
// unfriendly state, in order to fail as early as possible.

if !is_dlc_channel_confirmed().map_err(SubmitOrderError::Storage)? {
// TODO: Do not hard-code confirmations.
return Err(SubmitOrderError::UnconfirmedChannel {
current_confirmations: 0,
required_confirmations: 1,
});
}
}
check_channel_state()?;

// Having an order in `Filling` should mean that the subchannel is in the midst of an update.
// Since we currently only support one subchannel per app, it does not make sense to start
Expand Down Expand Up @@ -122,6 +116,67 @@ pub async fn submit_order(
Ok(order.id)
}

/// Checks if the channel is in a valid state to post the order.
///
/// Will fail in the following scenarios
/// 1. Open position, but no channel in state [`SignedChannelState::Established`]
/// 2. Open position and not enough confirmations on the funding txid.
/// 3. No position and a channel which is not in state [`SignedChannelState::Settled`]
fn check_channel_state() -> Result<(), SubmitOrderError> {
let channel = ln_dlc::get_signed_dlc_channel().map_err(SubmitOrderError::Storage)?;

if position::handler::get_positions()
.map_err(SubmitOrderError::Storage)?
.first()
.is_some()
{
match channel {
Some(SignedChannel {
state: SignedChannelState::Established { .. },
..
}) => {} // all good we can continue with the order
Some(channel) => {
return Err(SubmitOrderError::InvalidChannelState {
expected_channel_state: "Established".to_string(),
actual_channel_state: signed_channel_state_name(&channel),
})
}
None => {
return Err(SubmitOrderError::MissingChannel(
"Expected established dlc channel.".to_string(),
))
}
}

// If we have an open position, we should not allow any further trading until the current
// DLC channel is confirmed on-chain. Otherwise we can run into pesky DLC protocol
// failures.
if !is_dlc_channel_confirmed().map_err(SubmitOrderError::Storage)? {
// TODO: Do not hard-code confirmations.
return Err(SubmitOrderError::UnconfirmedChannel {
current_confirmations: 0,
required_confirmations: 1,
});
}
} else {
match channel {
None
| Some(SignedChannel {
state: SignedChannelState::Settled { .. },
..
}) => {} // all good we can continue with the order
Some(channel) => {
return Err(SubmitOrderError::InvalidChannelState {
expected_channel_state: "Settled".to_string(),
actual_channel_state: signed_channel_state_name(&channel),
});
}
}
}

Ok(())
}

pub(crate) fn async_order_filling(order: commons::Order, filled_with: FilledWith) -> Result<()> {
let order_type = match order.order_type {
commons::OrderType::Market => OrderType::Market,
Expand Down

0 comments on commit 71824f6

Please sign in to comment.