Skip to content

Commit

Permalink
feat: Eagerly sync wallet if signed channel is not yet confirmed
Browse files Browse the repository at this point in the history
  • Loading branch information
holzeis committed Jun 3, 2024
1 parent df7be13 commit 4e62b51
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 42 deletions.
3 changes: 2 additions & 1 deletion coordinator/src/node/liquidated_positions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ async fn check_if_positions_need_to_get_liquidated(
// liquidation.
match node
.inner
.is_signed_dlc_channel_confirmed_by_trader_id(position.trader)
.check_if_signed_channel_is_confirmed(position.trader)
.await
{
Ok(true) => {
tracing::debug!(trader_id=%position.trader, "Traders dlc channel is confirmed. Continuing with the liquidation");
Expand Down
18 changes: 14 additions & 4 deletions coordinator/src/trade/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,8 +804,13 @@ impl TradeExecutor {
trade_params: &TradeParams,
resize_action: ResizeAction,
) -> Result<()> {
if !self.node.inner.is_dlc_channel_confirmed(&dlc_channel_id)? {
bail!("Underlying DLC channel not yet confirmed");
if !self
.node
.inner
.check_if_signed_channel_is_confirmed(position.trader)
.await?
{
bail!("Underlying DLC channel not yet confirmed.");
}

let peer_id = trade_params.pubkey;
Expand Down Expand Up @@ -1045,8 +1050,13 @@ impl TradeExecutor {
trade_params: &TradeParams,
channel_id: DlcChannelId,
) -> Result<()> {
if !self.node.inner.is_dlc_channel_confirmed(&channel_id)? {
bail!("Underlying DLC channel not yet confirmed");
if !self
.node
.inner
.check_if_signed_channel_is_confirmed(position.trader)
.await?
{
bail!("Underlying DLC channel not yet confirmed.");
}

let closing_price = trade_params.average_execution_price();
Expand Down
42 changes: 27 additions & 15 deletions crates/xxi-node/src/node/dlc_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,28 +581,40 @@ impl<D: BdkStorage, S: TenTenOneStorage + 'static, N: LnDlcStorage + Sync + Send
Ok(dlc_channels)
}

pub fn is_signed_dlc_channel_confirmed_by_trader_id(
&self,
trader_id: PublicKey,
) -> Result<bool> {
let signed_channel = self.get_signed_channel_by_trader_id(trader_id)?;
self.is_dlc_channel_confirmed(&signed_channel.channel_id)
/// Checks if the underlying contract of the signed channel has been confirmed. If not a
/// periodic check is run to ensure we are on the latest state and return the corresponding
/// response.
pub async fn check_if_signed_channel_is_confirmed(&self, trader: PublicKey) -> Result<bool> {
let signed_channel = self.get_signed_channel_by_trader_id(trader)?;
if !self.is_dlc_channel_confirmed(&signed_channel.channel_id)? {
self.sync_on_chain_wallet().await?;
spawn_blocking({
let dlc_manager = self.dlc_manager.clone();
move || dlc_manager.periodic_check()
})
.await
.expect("task to complete")?;

return self.is_dlc_channel_confirmed(&signed_channel.channel_id);
}

Ok(true)
}

// TODO: This API could return the number of required confirmations + the number of current
// confirmations.
pub fn is_dlc_channel_confirmed(&self, dlc_channel_id: &DlcChannelId) -> Result<bool> {
fn is_contract_confirmed(&self, contract_id: &ContractId) -> Result<bool> {
let contract = self
.get_contract_by_id(contract_id)?
.context("Could not find contract for signed channel in state Established.")?;
Ok(matches!(contract, Contract::Confirmed { .. }))
}

fn is_dlc_channel_confirmed(&self, dlc_channel_id: &DlcChannelId) -> Result<bool> {
let channel = self.get_dlc_channel_by_id(dlc_channel_id)?;
let confirmed = match channel {
Channel::Signed(signed_channel) => match signed_channel.state {
SignedChannelState::Established {
signed_contract_id, ..
} => {
let contract = self.get_contract_by_id(&signed_contract_id)?.context(
"Could not find contract for signed channel in state Established.",
)?;
matches!(contract, Contract::Confirmed { .. })
}
} => self.is_contract_confirmed(&signed_contract_id)?,
_ => true,
},
Channel::Offered(_)
Expand Down
11 changes: 5 additions & 6 deletions mobile/native/src/dlc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -907,18 +907,17 @@ pub fn delete_dlc_channel(dlc_channel_id: &DlcChannelId) -> Result<()> {
Ok(())
}

pub fn is_dlc_channel_confirmed() -> Result<bool> {
pub async fn check_if_signed_channel_is_confirmed() -> Result<bool> {
let node = match state::try_get_node() {
Some(node) => node,
None => return Ok(false),
};

let dlc_channel = match get_signed_dlc_channel()? {
Some(dlc_channel) => dlc_channel,
None => return Ok(false),
};
let counterparty = config::get_coordinator_info().pubkey;

node.inner.is_dlc_channel_confirmed(&dlc_channel.channel_id)
node.inner
.check_if_signed_channel_is_confirmed(counterparty)
.await
}

pub fn get_fee_rate_for_target(target: ConfirmationTarget) -> FeeRate {
Expand Down
24 changes: 10 additions & 14 deletions mobile/native/src/trade/order/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::db;
use crate::db::get_order_in_filling;
use crate::db::maybe_get_open_orders;
use crate::dlc;
use crate::dlc::is_dlc_channel_confirmed;
use crate::dlc::check_if_signed_channel_is_confirmed;
use crate::event;
use crate::event::BackgroundTask;
use crate::event::EventInternal;
Expand Down Expand Up @@ -42,11 +42,8 @@ pub enum SubmitOrderError {
/// Generic problem related to the storage layer (sqlite, sled).
#[error("Storage failed: {0}")]
Storage(anyhow::Error),
#[error("DLC channel not yet confirmed: has {current_confirmations} confirmations, needs {required_confirmations}")]
UnconfirmedChannel {
current_confirmations: u64,
required_confirmations: u64,
},
#[error("DLC channel not yet confirmed")]
UnconfirmedChannel,
#[error("DLC Channel in invalid state: expected {expected_channel_state}, got {actual_channel_state}")]
InvalidChannelState {
expected_channel_state: String,
Expand Down Expand Up @@ -88,7 +85,7 @@ pub async fn submit_order_internal(
order: Order,
channel_opening_params: Option<ChannelOpeningParams>,
) -> Result<Uuid, SubmitOrderError> {
check_channel_state()?;
check_channel_state().await?;

// 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 @@ -142,7 +139,7 @@ pub async fn submit_order_internal(
/// 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> {
async fn check_channel_state() -> Result<(), SubmitOrderError> {
let channel = dlc::get_signed_dlc_channel().map_err(SubmitOrderError::Storage)?;

if position::handler::get_positions()
Expand Down Expand Up @@ -171,12 +168,11 @@ fn check_channel_state() -> Result<(), 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 !is_dlc_channel_confirmed().map_err(SubmitOrderError::Storage)? {
// TODO: Do not hard-code confirmations.
return Err(SubmitOrderError::UnconfirmedChannel {
current_confirmations: 0,
required_confirmations: 1,
});
if !check_if_signed_channel_is_confirmed()
.await
.map_err(SubmitOrderError::Storage)?
{
return Err(SubmitOrderError::UnconfirmedChannel);
}
} else {
match channel {
Expand Down
3 changes: 1 addition & 2 deletions webapp/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use native::api::WalletHistoryItemType;
use native::calculations::calculate_pnl;
use native::channel_trade_constraints;
use native::dlc;
use native::dlc::is_dlc_channel_confirmed;
use native::trade::order::FailureReason;
use native::trade::order::InvalidSubchannelOffer;
use rust_decimal::prelude::ToPrimitive;
Expand Down Expand Up @@ -358,7 +357,7 @@ pub async fn post_new_order(params: Json<NewOrderParams>) -> Result<Json<OrderId
.try_into()
.context("Could not parse order request")?;

let is_dlc_channel_confirmed = is_dlc_channel_confirmed()?;
let is_dlc_channel_confirmed = dlc::check_if_signed_channel_is_confirmed().await?;

let channel_opening_params = if is_dlc_channel_confirmed {
None
Expand Down

0 comments on commit 4e62b51

Please sign in to comment.