diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a3a31f84..e23868eee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - Feat(webapp): Show order history +- Fix: Add reject dlc channel, settle and renew offer +- Chore: Change pending offer policy to reject on reconnect ## [1.8.4] - 2024-01-31 diff --git a/Cargo.lock b/Cargo.lock index 6df841a7e..2523c0160 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1219,7 +1219,7 @@ dependencies = [ [[package]] name = "dlc" version = "0.4.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=69d63e1#69d63e113712cf9850c25643ed49816a283cb8a3" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=8c13abf4283244d0e40b042468f0ffea3c0081b4#8c13abf4283244d0e40b042468f0ffea3c0081b4" dependencies = [ "bitcoin", "miniscript 8.0.2", @@ -1231,7 +1231,7 @@ dependencies = [ [[package]] name = "dlc-manager" version = "0.4.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=69d63e1#69d63e113712cf9850c25643ed49816a283cb8a3" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=8c13abf4283244d0e40b042468f0ffea3c0081b4#8c13abf4283244d0e40b042468f0ffea3c0081b4" dependencies = [ "async-trait", "bitcoin", @@ -1247,7 +1247,7 @@ dependencies = [ [[package]] name = "dlc-messages" version = "0.4.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=69d63e1#69d63e113712cf9850c25643ed49816a283cb8a3" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=8c13abf4283244d0e40b042468f0ffea3c0081b4#8c13abf4283244d0e40b042468f0ffea3c0081b4" dependencies = [ "bitcoin", "dlc", @@ -1260,7 +1260,7 @@ dependencies = [ [[package]] name = "dlc-trie" version = "0.4.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=69d63e1#69d63e113712cf9850c25643ed49816a283cb8a3" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=8c13abf4283244d0e40b042468f0ffea3c0081b4#8c13abf4283244d0e40b042468f0ffea3c0081b4" dependencies = [ "bitcoin", "dlc", @@ -2241,7 +2241,7 @@ dependencies = [ "time", "tokio", "tracing", - "tracing-log", + "tracing-log 0.1.3", "tracing-subscriber", "ureq", "uuid", @@ -2548,6 +2548,7 @@ dependencies = [ "tokio-tungstenite", "tokio-util", "tracing", + "tracing-log 0.2.0", "tracing-subscriber", "trade", "uuid", @@ -2819,7 +2820,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "p2pd-oracle-client" version = "0.1.0" -source = "git+https://github.com/p2pderivatives/rust-dlc?rev=69d63e1#69d63e113712cf9850c25643ed49816a283cb8a3" +source = "git+https://github.com/p2pderivatives/rust-dlc?rev=8c13abf4283244d0e40b042468f0ffea3c0081b4#8c13abf4283244d0e40b042468f0ffea3c0081b4" dependencies = [ "chrono", "dlc-manager", @@ -4458,6 +4459,17 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-serde" version = "0.1.3" @@ -4486,7 +4498,7 @@ dependencies = [ "time", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.1.3", "tracing-serde", ] diff --git a/Cargo.toml b/Cargo.toml index ca568761b..c03531dc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,11 +24,11 @@ resolver = "2" # `p2pderivatives/rust-dlc#feature/ln-dlc-channels`: 4e104b4. This patch ensures backwards # compatibility for 10101 through the `rust-lightning:0.0.116` upgrade. We will be able to drop it # once all users have been upgraded and traded once. -dlc-manager = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "69d63e1" } -dlc-messages = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "69d63e1" } -dlc = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "69d63e1" } -p2pd-oracle-client = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "69d63e1" } -dlc-trie = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "69d63e1" } +dlc-manager = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "8c13abf4283244d0e40b042468f0ffea3c0081b4" } +dlc-messages = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "8c13abf4283244d0e40b042468f0ffea3c0081b4" } +dlc = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "8c13abf4283244d0e40b042468f0ffea3c0081b4" } +p2pd-oracle-client = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "8c13abf4283244d0e40b042468f0ffea3c0081b4" } +dlc-trie = { git = "https://github.com/p2pderivatives/rust-dlc", rev = "8c13abf4283244d0e40b042468f0ffea3c0081b4" } # We should usually track the `p2pderivatives/split-tx-experiment[-10101]` branch. For now we depend on a special fork which removes a panic in rust-lightning lightning = { git = "https://github.com/bonomat/rust-lightning-p2p-derivatives", rev = "e49030e" } diff --git a/coordinator/src/bin/coordinator.rs b/coordinator/src/bin/coordinator.rs index 8890b798a..6edf2d46f 100644 --- a/coordinator/src/bin/coordinator.rs +++ b/coordinator/src/bin/coordinator.rs @@ -143,8 +143,10 @@ async fn main() -> Result<()> { )?); let dlc_handler = DlcHandler::new(pool.clone(), node.clone()); - let _handle = - dlc_handler::spawn_handling_dlc_messages(dlc_handler, node_event_handler.subscribe()); + let _handle = dlc_handler::spawn_handling_outbound_dlc_messages( + dlc_handler, + node_event_handler.subscribe(), + ); let event_handler = CoordinatorEventHandler::new(node.clone(), Some(node_event_sender)); let running = node.start(event_handler, false)?; diff --git a/coordinator/src/db/positions.rs b/coordinator/src/db/positions.rs index 8b0ed1bd2..8f46e6538 100644 --- a/coordinator/src/db/positions.rs +++ b/coordinator/src/db/positions.rs @@ -144,6 +144,29 @@ impl Position { Ok(()) } + /// sets the status of the position in state `Closing` to a new state + pub fn update_closing_position( + conn: &mut PgConnection, + trader_pubkey: String, + state: crate::position::models::PositionState, + ) -> Result<()> { + let state = PositionState::from(state); + let affected_rows = diesel::update(positions::table) + .filter(positions::trader_pubkey.eq(trader_pubkey.clone())) + .filter(positions::position_state.eq(PositionState::Closing)) + .set(( + positions::position_state.eq(state), + positions::update_timestamp.eq(OffsetDateTime::now_utc()), + )) + .execute(conn)?; + + if affected_rows == 0 { + bail!("Could not update position to {state:?} for {trader_pubkey}") + } + + Ok(()) + } + /// sets the status of all open position to closing (note, we expect that number to be always /// exactly 1) pub fn set_open_position_to_closing( diff --git a/coordinator/src/dlc_handler.rs b/coordinator/src/dlc_handler.rs index 12e6b62d0..28c6365bd 100644 --- a/coordinator/src/dlc_handler.rs +++ b/coordinator/src/dlc_handler.rs @@ -42,12 +42,10 @@ impl DlcHandler { } } -/// [`spawn_handling_dlc_messages`] handles sending outbound dlc messages as well as keeping track -/// of what dlc messages have already been processed and what was the last outbound dlc message -/// so it can be resend on reconnect. -/// -/// It does not handle the incoming dlc messages! -pub fn spawn_handling_dlc_messages( +/// [`spawn_handling_outbound_dlc_messages`] handles sending outbound dlc messages as well as +/// keeping track of what dlc messages have already been processed and what was the last outbound +/// dlc message so it can be resend on reconnect. +pub fn spawn_handling_outbound_dlc_messages( dlc_handler: DlcHandler, mut receiver: broadcast::Receiver, ) -> RemoteHandle<()> { diff --git a/coordinator/src/node.rs b/coordinator/src/node.rs index db1081fdf..645c74222 100644 --- a/coordinator/src/node.rs +++ b/coordinator/src/node.rs @@ -27,6 +27,7 @@ use diesel::Connection; use diesel::PgConnection; use dlc_manager::channel::signed_channel::SignedChannel; use dlc_manager::channel::signed_channel::SignedChannelState; +use dlc_manager::channel::Channel; use dlc_manager::contract::contract_input::ContractInput; use dlc_manager::contract::contract_input::ContractInputInfo; use dlc_manager::contract::contract_input::OracleInput; @@ -663,7 +664,7 @@ impl Node { let trader_peer_id = trade_params.pubkey; match self .inner - .get_dlc_channel_by_counterparty(&trader_peer_id)? + .get_signed_dlc_channel_by_counterparty(&trader_peer_id)? { None => { ensure!( @@ -912,6 +913,68 @@ impl Node { PositionState::Open, )?; } + ChannelMessage::Reject(reject) => { + // TODO(holzeis): if an dlc channel gets rejected we have to deal with the + // counterparty as well. + + let channel_id_hex_string = reject.channel_id.to_hex(); + + let channel = self.inner.get_dlc_channel_by_id(&reject.channel_id)?; + let mut connection = self.pool.get()?; + + match channel { + Channel::Cancelled(_) => { + tracing::info!( + channel_id = channel_id_hex_string, + node_id = node_id.to_string(), + "DLC Channel offer has been rejected. Setting position to failed." + ); + + db::positions::Position::update_proposed_position( + &mut connection, + node_id.to_string(), + PositionState::Failed, + )?; + } + Channel::Signed(SignedChannel { + state: SignedChannelState::Established { .. }, + .. + }) => { + // TODO(holzeis): Reverting the position state back from `Closing` + // to `Open` only works as long as we do not support resizing. This + // logic needs to be adapted when we implement resize. + + tracing::info!( + channel_id = channel_id_hex_string, + node_id = node_id.to_string(), + "DLC Channel settle offer has been rejected. Setting position to back to open." + ); + + db::positions::Position::update_closing_position( + &mut connection, + node_id.to_string(), + PositionState::Open, + )?; + } + Channel::Signed(SignedChannel { + state: SignedChannelState::Settled { .. }, + .. + }) => { + tracing::info!( + channel_id = channel_id_hex_string, + node_id = node_id.to_string(), + "DLC Channel renew offer has been rejected. Setting position to failed." + ); + + db::positions::Position::update_proposed_position( + &mut connection, + node_id.to_string(), + PositionState::Failed, + )?; + } + _ => {} + } + } _ => {} }; diff --git a/crates/ln-dlc-node/src/dlc_message.rs b/crates/ln-dlc-node/src/dlc_message.rs index 84e625190..470c31a67 100644 --- a/crates/ln-dlc-node/src/dlc_message.rs +++ b/crates/ln-dlc-node/src/dlc_message.rs @@ -50,76 +50,12 @@ impl SerializedDlcMessage { } } -#[derive(Hash, Clone, Debug)] -pub enum DlcMessageType { - Offer, - Accept, - Sign, - SettleOffer, - SettleAccept, - SettleConfirm, - SettleFinalize, - RenewOffer, - RenewAccept, - RenewConfirm, - RenewFinalize, - RenewRevoke, - CollaborativeCloseOffer, - Reject, -} - impl TryFrom<&SerializedDlcMessage> for Message { type Error = anyhow::Error; fn try_from(serialized_msg: &SerializedDlcMessage) -> Result { - let message = match serialized_msg.clone().message_type { - DlcMessageType::Offer => Message::Channel(ChannelMessage::Offer(serde_json::from_str( - &serialized_msg.message, - )?)), - DlcMessageType::Accept => Message::Channel(ChannelMessage::Accept( - serde_json::from_str(&serialized_msg.message)?, - )), - DlcMessageType::Sign => Message::Channel(ChannelMessage::Sign(serde_json::from_str( - &serialized_msg.message, - )?)), - DlcMessageType::SettleOffer => Message::Channel(ChannelMessage::SettleOffer( - serde_json::from_str(&serialized_msg.message)?, - )), - DlcMessageType::SettleAccept => Message::Channel(ChannelMessage::SettleAccept( - serde_json::from_str(&serialized_msg.message)?, - )), - DlcMessageType::SettleConfirm => Message::Channel(ChannelMessage::SettleConfirm( - serde_json::from_str(&serialized_msg.message)?, - )), - DlcMessageType::SettleFinalize => Message::Channel(ChannelMessage::SettleFinalize( - serde_json::from_str(&serialized_msg.message)?, - )), - DlcMessageType::RenewOffer => Message::Channel(ChannelMessage::RenewOffer( - serde_json::from_str(&serialized_msg.message)?, - )), - DlcMessageType::RenewAccept => Message::Channel(ChannelMessage::RenewAccept( - serde_json::from_str(&serialized_msg.message)?, - )), - DlcMessageType::RenewConfirm => Message::Channel(ChannelMessage::RenewConfirm( - serde_json::from_str(&serialized_msg.message)?, - )), - DlcMessageType::RenewFinalize => Message::Channel(ChannelMessage::RenewFinalize( - serde_json::from_str(&serialized_msg.message)?, - )), - DlcMessageType::RenewRevoke => Message::Channel(ChannelMessage::RenewRevoke( - serde_json::from_str(&serialized_msg.message)?, - )), - DlcMessageType::CollaborativeCloseOffer => { - Message::Channel(ChannelMessage::CollaborativeCloseOffer( - serde_json::from_str(&serialized_msg.message)?, - )) - } - DlcMessageType::Reject => Message::Channel(ChannelMessage::Reject( - serde_json::from_str(&serialized_msg.message)?, - )), - }; - - Ok(message) + let message: ChannelMessage = serde_json::from_str(&serialized_msg.message)?; + Ok(Message::Channel(message)) } } @@ -127,59 +63,13 @@ impl TryFrom<&Message> for SerializedDlcMessage { type Error = anyhow::Error; fn try_from(msg: &Message) -> Result { - let (message, message_type) = match &msg { - Message::Channel(message) => match message { - ChannelMessage::Offer(offer) => { - (serde_json::to_string(&offer)?, DlcMessageType::Offer) - } - ChannelMessage::Accept(accept) => { - (serde_json::to_string(&accept)?, DlcMessageType::Accept) - } - ChannelMessage::Sign(sign) => (serde_json::to_string(&sign)?, DlcMessageType::Sign), - ChannelMessage::SettleOffer(settle_offer) => ( - serde_json::to_string(&settle_offer)?, - DlcMessageType::SettleOffer, - ), - ChannelMessage::SettleAccept(settle_accept) => ( - serde_json::to_string(&settle_accept)?, - DlcMessageType::SettleAccept, - ), - ChannelMessage::SettleConfirm(settle_confirm) => ( - serde_json::to_string(&settle_confirm)?, - DlcMessageType::SettleConfirm, - ), - ChannelMessage::SettleFinalize(settle_finalize) => ( - serde_json::to_string(&settle_finalize)?, - DlcMessageType::SettleFinalize, - ), - ChannelMessage::RenewOffer(renew_offer) => ( - serde_json::to_string(&renew_offer)?, - DlcMessageType::RenewOffer, - ), - ChannelMessage::RenewAccept(renew_accept) => ( - serde_json::to_string(&renew_accept)?, - DlcMessageType::RenewAccept, - ), - ChannelMessage::RenewConfirm(renew_confirm) => ( - serde_json::to_string(&renew_confirm)?, - DlcMessageType::RenewConfirm, - ), - ChannelMessage::RenewFinalize(renew_finalize) => ( - serde_json::to_string(&renew_finalize)?, - DlcMessageType::RenewFinalize, - ), - ChannelMessage::RenewRevoke(renew_revoke) => ( - serde_json::to_string(&renew_revoke)?, - DlcMessageType::RenewRevoke, - ), - ChannelMessage::CollaborativeCloseOffer(collaborative_close_offer) => ( - serde_json::to_string(&collaborative_close_offer)?, - DlcMessageType::CollaborativeCloseOffer, - ), - ChannelMessage::Reject(reject) => { - (serde_json::to_string(&reject)?, DlcMessageType::Reject) - } - }, + let (message, message_type) = match msg { + Message::Channel(message) => { + let message_type = DlcMessageType::from(message); + let message = serde_json::to_string(&message)?; + + (message, message_type) + } _ => unreachable!(), }; @@ -189,3 +79,42 @@ impl TryFrom<&Message> for SerializedDlcMessage { }) } } + +#[derive(Hash, Clone, Debug)] +pub enum DlcMessageType { + Offer, + Accept, + Sign, + SettleOffer, + SettleAccept, + SettleConfirm, + SettleFinalize, + RenewOffer, + RenewAccept, + RenewConfirm, + RenewFinalize, + RenewRevoke, + CollaborativeCloseOffer, + Reject, +} + +impl From<&ChannelMessage> for DlcMessageType { + fn from(value: &ChannelMessage) -> Self { + match value { + ChannelMessage::Offer(_) => DlcMessageType::Offer, + ChannelMessage::Accept(_) => DlcMessageType::Accept, + ChannelMessage::Sign(_) => DlcMessageType::Sign, + ChannelMessage::SettleOffer(_) => DlcMessageType::SettleOffer, + ChannelMessage::SettleAccept(_) => DlcMessageType::SettleAccept, + ChannelMessage::SettleConfirm(_) => DlcMessageType::SettleConfirm, + ChannelMessage::SettleFinalize(_) => DlcMessageType::SettleFinalize, + ChannelMessage::RenewOffer(_) => DlcMessageType::RenewOffer, + ChannelMessage::RenewAccept(_) => DlcMessageType::RenewAccept, + ChannelMessage::RenewConfirm(_) => DlcMessageType::RenewConfirm, + ChannelMessage::RenewFinalize(_) => DlcMessageType::RenewFinalize, + ChannelMessage::RenewRevoke(_) => DlcMessageType::RenewRevoke, + ChannelMessage::CollaborativeCloseOffer(_) => DlcMessageType::CollaborativeCloseOffer, + ChannelMessage::Reject(_) => DlcMessageType::Reject, + } + } +} diff --git a/crates/ln-dlc-node/src/ln/dlc_channel_details.rs b/crates/ln-dlc-node/src/ln/dlc_channel_details.rs index 614619527..3362b12d0 100644 --- a/crates/ln-dlc-node/src/ln/dlc_channel_details.rs +++ b/crates/ln-dlc-node/src/ln/dlc_channel_details.rs @@ -46,6 +46,7 @@ pub enum ChannelState { CollaborativelyClosed, FailedAccept, FailedSign, + Cancelled, } impl From for DlcChannelDetails { @@ -85,6 +86,7 @@ impl From for ChannelState { Channel::CollaborativelyClosed(_) => ChannelState::CollaborativelyClosed, Channel::FailedAccept(_) => ChannelState::FailedAccept, Channel::FailedSign(_) => ChannelState::FailedSign, + Channel::Cancelled(_) => ChannelState::Cancelled, } } } diff --git a/crates/ln-dlc-node/src/node/dlc_channel.rs b/crates/ln-dlc-node/src/node/dlc_channel.rs index 441f9dd1f..b8a41caf0 100644 --- a/crates/ln-dlc-node/src/node/dlc_channel.rs +++ b/crates/ln-dlc-node/src/node/dlc_channel.rs @@ -21,7 +21,6 @@ use dlc_manager::contract::ContractDescriptor; use dlc_manager::DlcChannelId; use dlc_manager::Oracle; use dlc_manager::Storage; -use dlc_messages::channel::Reject; use dlc_messages::ChannelMessage; use dlc_messages::Message; use time::OffsetDateTime; @@ -87,27 +86,6 @@ impl Nod .await? } - pub fn reject_dlc_channel_offer(&self, channel_id: &DlcChannelId) -> Result<()> { - let channel_id_hex = hex::encode(channel_id); - - let channel = self - .dlc_manager - .get_store() - .get_channel(channel_id)? - .with_context(|| format!("Couldn't find channel by id. {}", channel_id.to_hex()))?; - - tracing::info!(channel_id = %channel_id_hex, "Rejecting DLC channel offer"); - - self.event_handler.publish(NodeEvent::SendDlcMessage { - peer: channel.get_counter_party_id(), - msg: Message::Channel(ChannelMessage::Reject(Reject { - channel_id: channel.get_id(), - })), - })?; - - Ok(()) - } - pub fn accept_dlc_channel_offer(&self, channel_id: &DlcChannelId) -> Result<()> { let channel_id_hex = hex::encode(channel_id); @@ -273,9 +251,8 @@ impl Nod tracing::info!(channel_id = %hex::encode(dlc_channel_id), "Proposing a DLC channel update"); spawn_blocking({ let dlc_manager = self.dlc_manager.clone(); - let dlc_message_handler = self.dlc_message_handler.clone(); - let peer_manager = self.peer_manager.clone(); let dlc_channel_id = *dlc_channel_id; + let event_handler = self.event_handler.clone(); move || { // Not actually needed. See https://github.com/p2pderivatives/rust-dlc/issues/149. let counter_payout = 0; @@ -283,12 +260,10 @@ impl Nod let (renew_offer, counterparty_pubkey) = dlc_manager.renew_offer(&dlc_channel_id, counter_payout, &contract_input)?; - send_dlc_message( - &dlc_message_handler, - &peer_manager, - counterparty_pubkey, - Message::Channel(ChannelMessage::RenewOffer(renew_offer)), - ); + event_handler.publish(NodeEvent::SendDlcMessage { + msg: Message::Channel(ChannelMessage::RenewOffer(renew_offer)), + peer: counterparty_pubkey, + })?; let offered_contracts = dlc_manager.get_store().get_contract_offers()?; @@ -336,9 +311,9 @@ impl Nod /// Will return an error if the contract is not yet signed or confirmed on-chain. pub fn get_expiry_for_confirmed_dlc_channel( &self, - dlc_channel_id: DlcChannelId, + dlc_channel_id: &DlcChannelId, ) -> Result { - match self.get_contract_by_dlc_channel_id(&dlc_channel_id)? { + match self.get_contract_by_dlc_channel_id(dlc_channel_id)? { Contract::Signed(contract) | Contract::Confirmed(contract) => { let offered_contract = contract.accepted_contract.offered_contract; let contract_info = offered_contract @@ -376,7 +351,7 @@ impl Nod }) } - pub fn get_dlc_channel_by_counterparty( + pub fn get_signed_dlc_channel_by_counterparty( &self, counterparty_pk: &PublicKey, ) -> Result> { diff --git a/crates/ln-dlc-storage/src/lib.rs b/crates/ln-dlc-storage/src/lib.rs index e54861743..74341810a 100644 --- a/crates/ln-dlc-storage/src/lib.rs +++ b/crates/ln-dlc-storage/src/lib.rs @@ -155,7 +155,8 @@ convertible_enum!( ClosedPunished, CollaborativelyClosed, FailedAccept, - FailedSign,; + FailedSign, + Cancelled,; }, Channel ); @@ -767,6 +768,7 @@ fn serialize_channel(channel: &Channel) -> Result, ::std::io::Error> { c.serialize() } Channel::ClosedPunished(c) => c.serialize(), + Channel::Cancelled(o) => o.serialize(), }; let mut serialized = serialized?; let mut res = Vec::with_capacity(serialized.len() + 1); @@ -816,6 +818,9 @@ fn deserialize_channel(buff: &Vec) -> Result { ChannelPrefix::ClosedPunished => Channel::ClosedPunished( ClosedPunishedChannel::deserialize(&mut cursor).map_err(to_storage_error)?, ), + ChannelPrefix::Cancelled => { + Channel::Cancelled(OfferedChannel::deserialize(&mut cursor).map_err(to_storage_error)?) + } }; Ok(channel) } diff --git a/crates/payout_curve/Cargo.toml b/crates/payout_curve/Cargo.toml index 95b170a2c..4bc8f0d17 100644 --- a/crates/payout_curve/Cargo.toml +++ b/crates/payout_curve/Cargo.toml @@ -13,7 +13,7 @@ trade = { path = "../trade" } [dev-dependencies] csv = "1.3.0" -dlc-manager = "0.4.0" +dlc-manager = { version = "0.4.0", features = ["use-serde"] } proptest = "1" rust_decimal_macros = "1" tracing = "0.1.37" diff --git a/crates/tests-e2e/src/coordinator.rs b/crates/tests-e2e/src/coordinator.rs index ef02ef3df..2c28790b4 100644 --- a/crates/tests-e2e/src/coordinator.rs +++ b/crates/tests-e2e/src/coordinator.rs @@ -118,6 +118,7 @@ pub enum ChannelState { CollaborativelyClosed, FailedAccept, FailedSign, + Cancelled, } #[derive(Deserialize, Debug)] diff --git a/crates/tests-e2e/src/setup.rs b/crates/tests-e2e/src/setup.rs index 2d74d6822..0e3ae46e6 100644 --- a/crates/tests-e2e/src/setup.rs +++ b/crates/tests-e2e/src/setup.rs @@ -23,7 +23,7 @@ pub struct TestSetup { impl TestSetup { /// Start test with a running app and a funded wallet. - pub async fn new_after_funding() -> Self { + pub async fn new_after_funding(fund_amount: Option) -> Self { init_tracing(); let client = init_reqwest(); @@ -62,13 +62,13 @@ impl TestSetup { "App should start with empty off-chain wallet" ); - let fund_amount = Amount::ONE_BTC; + let fund_amount = fund_amount.unwrap_or(Amount::ONE_BTC); let address = api::get_unused_address(); let address = &address.0.parse().unwrap(); bitcoind - .send_to_address(address, Amount::ONE_BTC) + .send_to_address(address, fund_amount) .await .unwrap(); @@ -92,7 +92,7 @@ impl TestSetup { /// Start test with a running app with a funded wallet and an open position. pub async fn new_with_open_position() -> Self { - let setup = Self::new_after_funding().await; + let setup = Self::new_after_funding(None).await; let rx = &setup.app.rx; tracing::info!("Opening a position"); diff --git a/crates/tests-e2e/tests/basic.rs b/crates/tests-e2e/tests/basic.rs index 53afb9d43..f3d8f281e 100644 --- a/crates/tests-e2e/tests/basic.rs +++ b/crates/tests-e2e/tests/basic.rs @@ -4,7 +4,7 @@ use tests_e2e::setup::TestSetup; #[tokio::test(flavor = "multi_thread")] #[ignore = "need to be run with 'just e2e' command"] async fn app_can_be_funded_with_bitcoind() -> Result<()> { - TestSetup::new_after_funding().await; + TestSetup::new_after_funding(None).await; Ok(()) } diff --git a/crates/tests-e2e/tests/open_position.rs b/crates/tests-e2e/tests/open_position.rs index f4c66b97b..c5a53eb0a 100644 --- a/crates/tests-e2e/tests/open_position.rs +++ b/crates/tests-e2e/tests/open_position.rs @@ -23,7 +23,7 @@ fn dummy_order() -> NewOrder { #[tokio::test(flavor = "multi_thread")] #[ignore = "need to be run with 'just e2e' command"] async fn can_open_position() { - let test = TestSetup::new_after_funding().await; + let test = TestSetup::new_after_funding(None).await; let app = &test.app; let order = dummy_order(); diff --git a/crates/tests-e2e/tests/reject_offer.rs b/crates/tests-e2e/tests/reject_offer.rs new file mode 100644 index 000000000..3bcc8c371 --- /dev/null +++ b/crates/tests-e2e/tests/reject_offer.rs @@ -0,0 +1,101 @@ +use bitcoin::Amount; +use native::api; +use native::api::ContractSymbol; +use native::health::Service; +use native::health::ServiceStatus; +use native::trade::order::api::NewOrder; +use native::trade::order::api::OrderType; +use native::trade::order::OrderState; +use native::trade::position::PositionState; +use tests_e2e::setup::TestSetup; +use tests_e2e::wait_until; +use tokio::task::spawn_blocking; + +#[tokio::test(flavor = "multi_thread")] +#[ignore = "need to be run with 'just e2e' command"] +async fn reject_offer() { + let test = TestSetup::new_after_funding(Some(Amount::from_sat(250_000))).await; + let app = &test.app; + + let invalid_order = NewOrder { + leverage: 1.0, + contract_symbol: ContractSymbol::BtcUsd, + direction: api::Direction::Long, + quantity: 2000.0, + order_type: Box::new(OrderType::Market), + stable: false, + }; + + // submit order for which the app does not have enough liquidity. will fail with `Failed to + // accept dlc channel offer. Invalid state: Not enough UTXOs for amount` + spawn_blocking({ + let order = invalid_order.clone(); + move || api::submit_order(order).unwrap() + }) + .await + .unwrap(); + + assert_eq!(app.rx.status(Service::Orderbook), ServiceStatus::Online); + assert_eq!(app.rx.status(Service::Coordinator), ServiceStatus::Online); + + // Assert that the order was posted + wait_until!(app.rx.order().is_some()); + assert_eq!(app.rx.order().unwrap().quantity, invalid_order.quantity); + assert_eq!(app.rx.order().unwrap().direction, invalid_order.direction); + assert_eq!( + app.rx.order().unwrap().contract_symbol, + invalid_order.contract_symbol + ); + assert_eq!(app.rx.order().unwrap().leverage, invalid_order.leverage); + + // Assert that the order failed + wait_until!(matches!( + app.rx.order().unwrap().state, + OrderState::Failed { .. } + )); + + // Assert that no position has been opened + wait_until!(app.rx.position().is_none()); + + // Retry with a smaller order + let order = NewOrder { + leverage: 2.0, + contract_symbol: ContractSymbol::BtcUsd, + direction: api::Direction::Long, + quantity: 100.0, + order_type: Box::new(OrderType::Market), + stable: false, + }; + + spawn_blocking({ + let order = order.clone(); + move || api::submit_order(order).unwrap() + }) + .await + .unwrap(); + + // Assert that the order was posted + wait_until!(app.rx.order().is_some()); + assert_eq!(app.rx.order().unwrap().quantity, order.quantity); + assert_eq!(app.rx.order().unwrap().direction, order.direction); + assert_eq!( + app.rx.order().unwrap().contract_symbol, + order.contract_symbol + ); + assert_eq!(app.rx.order().unwrap().leverage, order.leverage); + + // Assert that the position is opened in the app + wait_until!(app.rx.position().is_some()); + assert_eq!(app.rx.position().unwrap().quantity, order.quantity); + assert_eq!(app.rx.position().unwrap().direction, order.direction); + assert_eq!( + app.rx.position().unwrap().contract_symbol, + order.contract_symbol + ); + assert_eq!(app.rx.position().unwrap().leverage, order.leverage); + wait_until!(app.rx.position().unwrap().position_state == PositionState::Open); + + // TODO(holzeis): Add reject tests for SettleOffer and RenewOffer. + // Unfortunately its not easy to provoke a reject for a settle offer or renew offer from a grey + // box integration test. +} diff --git a/mobile/native/Cargo.toml b/mobile/native/Cargo.toml index 5803951f9..67ff7537d 100644 --- a/mobile/native/Cargo.toml +++ b/mobile/native/Cargo.toml @@ -44,6 +44,7 @@ tokio = { version = "1.25.0", features = ["macros", "rt", "rt-multi-thread", "sy tokio-tungstenite = { version = "0.20", features = ["native-tls"] } tokio-util = { version = "0.7", features = ["io", "codec"] } tracing = "0.1.37" +tracing-log = "0.2.0" tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "env-filter", "time", "json"] } trade = { path = "../../crates/trade" } uuid = { version = "1.3.0", features = ["v4", "fast-rng", "macro-diagnostics"] } diff --git a/mobile/native/src/dlc_handler.rs b/mobile/native/src/dlc_handler.rs index a8ad338b0..be3c9a370 100644 --- a/mobile/native/src/dlc_handler.rs +++ b/mobile/native/src/dlc_handler.rs @@ -3,8 +3,8 @@ use crate::event; use crate::event::BackgroundTask; use crate::event::EventInternal; use crate::event::TaskStatus; -use crate::ln_dlc::node::NodeStorage; -use crate::storage::TenTenOneNodeStorage; +use crate::ln_dlc::node::Node; +use anyhow::Context; use anyhow::Result; use bitcoin::secp256k1::PublicKey; use dlc_messages::Message; @@ -16,7 +16,6 @@ use ln_dlc_node::node::rust_dlc_manager::channel::offered_channel::OfferedChanne use ln_dlc_node::node::rust_dlc_manager::channel::signed_channel::SignedChannel; use ln_dlc_node::node::rust_dlc_manager::channel::signed_channel::SignedChannelState; use ln_dlc_node::node::rust_dlc_manager::channel::Channel; -use ln_dlc_node::node::Node; use std::sync::Arc; use tokio::sync::broadcast; use tokio::sync::broadcast::error::RecvError; @@ -27,18 +26,21 @@ use tokio::sync::broadcast::error::RecvError; /// 1. Mark all received inbound messages as processed. /// 2. Save the last outbound dlc message, so it can be resend on the next reconnect. /// 3. Check if a receive message has already been processed and if so inform to skip the message. - #[derive(Clone)] pub struct DlcHandler { - node: Arc>, + node: Arc, } impl DlcHandler { - pub fn new(node: Arc>) -> Self { + pub fn new(node: Arc) -> Self { DlcHandler { node } } } -pub async fn handle_dlc_messages( + +/// Handles sending outbound dlc messages as well as keeping track of what +/// dlc messages have already been processed and what was the last outbound dlc message +/// so it can be resend on reconnect. +pub async fn handle_outbound_dlc_messages( dlc_handler: DlcHandler, mut receiver: broadcast::Receiver, ) { @@ -80,8 +82,8 @@ impl DlcHandler { )?; send_dlc_message( - &self.node.dlc_message_handler, - &self.node.peer_manager, + &self.node.inner.dlc_message_handler, + &self.node.inner.peer_manager, peer, msg, ); @@ -92,6 +94,7 @@ impl DlcHandler { pub fn on_connect(&self, peer: PublicKey) -> Result<()> { let dlc_channels: Vec = self .node + .inner .list_dlc_channels()? .into_iter() .filter(|c| { @@ -115,22 +118,20 @@ impl DlcHandler { temporary_channel_id, .. }) => { - tracing::info!("Accepting pending dlc channel offer."); + tracing::info!("Rejecting pending dlc channel offer."); // Pending dlc channel offer not yet confirmed on-chain event::publish(&EventInternal::BackgroundNotification( BackgroundTask::RecoverDlc(TaskStatus::Pending), )); - if let Err(e) = self.node.accept_dlc_channel_offer(temporary_channel_id) { - tracing::error!("Failed to accept pending dlc channel offer. {e:#}"); - tracing::warn!("Rejecting pending dlc channel offer!"); - self.node.reject_dlc_channel_offer(temporary_channel_id)?; + self.node + .reject_dlc_channel_offer(temporary_channel_id) + .context("Failed to reject pending dlc channel offer")?; - event::publish(&EventInternal::BackgroundNotification( - BackgroundTask::RecoverDlc(TaskStatus::Success), - )); - } + event::publish(&EventInternal::BackgroundNotification( + BackgroundTask::RecoverDlc(TaskStatus::Success), + )); return Ok(()); } @@ -139,7 +140,7 @@ impl DlcHandler { state: SignedChannelState::SettledReceived { .. }, .. }) => { - tracing::info!("Accepting pending dlc channel settle offer."); + tracing::info!("Rejecting pending dlc channel settle offer."); // Pending dlc channel settle offer with a dlc channel already confirmed // on-chain @@ -148,19 +149,35 @@ impl DlcHandler { )); self.node - .accept_dlc_channel_collaborative_settlement(channel_id)?; + .reject_settle_offer(channel_id) + .context("Failed to reject pending settle offer")?; + + event::publish(&EventInternal::BackgroundNotification( + BackgroundTask::RecoverDlc(TaskStatus::Success), + )); return Ok(()); } Channel::Signed(SignedChannel { - channel_id: _, + channel_id, state: SignedChannelState::RenewOffered { .. }, .. }) => { + tracing::info!("Rejecting pending dlc channel renew offer."); // Pending dlc channel renew (resize) offer with a dlc channel already confirmed // on-chain - // TODO: implement with resizing a position. + event::publish(&EventInternal::BackgroundNotification( + BackgroundTask::RecoverDlc(TaskStatus::Pending), + )); + + self.node + .reject_renew_offer(channel_id) + .context("Failed to reject pending renew offer")?; + + event::publish(&EventInternal::BackgroundNotification( + BackgroundTask::RecoverDlc(TaskStatus::Success), + )); return Ok(()); } @@ -175,6 +192,7 @@ impl DlcHandler { // TODO(bonomat): we should verify that the proposed amount is acceptable self.node + .inner .accept_dlc_channel_collaborative_close(channel_id)?; return Ok(()); @@ -206,8 +224,8 @@ impl DlcHandler { let message = Message::try_from(&last_outbound_serialized_dlc_message)?; send_dlc_message( - &self.node.dlc_message_handler, - &self.node.peer_manager, + &self.node.inner.dlc_message_handler, + &self.node.inner.peer_manager, peer, message, ); diff --git a/mobile/native/src/ln_dlc/mod.rs b/mobile/native/src/ln_dlc/mod.rs index 424899498..749c367dc 100644 --- a/mobile/native/src/ln_dlc/mod.rs +++ b/mobile/native/src/ln_dlc/mod.rs @@ -341,20 +341,16 @@ pub fn run(seed_dir: String, runtime: &Runtime) -> Result<()> { )?; let node = Arc::new(node); - let dlc_handler = DlcHandler::new(node.clone()); - runtime.spawn(async move { - // this handles sending outbound dlc messages as well as keeping track of what - // dlc messages have already been processed and what was the last outbound dlc message - // so it can be resend on reconnect. - // - // this does not handle the incoming dlc messages! - dlc_handler::handle_dlc_messages(dlc_handler, node_event_handler.subscribe()).await - }); - let event_handler = AppEventHandler::new(node.clone(), Some(event_sender)); let _running = node.start(event_handler, true)?; let node = Arc::new(Node::new(node, _running)); + let dlc_handler = DlcHandler::new(node.clone()); + runtime.spawn(async move { + dlc_handler::handle_outbound_dlc_messages(dlc_handler, node_event_handler.subscribe()) + .await + }); + // Refresh the wallet balance and history eagerly so that it can complete before the // triggering the first on-chain sync. This ensures that the UI appears ready as soon as // possible. diff --git a/mobile/native/src/ln_dlc/node.rs b/mobile/native/src/ln_dlc/node.rs index 1cefbe287..2c61a2b00 100644 --- a/mobile/native/src/ln_dlc/node.rs +++ b/mobile/native/src/ln_dlc/node.rs @@ -21,7 +21,6 @@ use bitcoin::Txid; use dlc_messages::ChannelMessage; use dlc_messages::Message; use lightning::chain::transaction::OutPoint; -use lightning::ln::ChannelId; use lightning::ln::PaymentHash; use lightning::ln::PaymentPreimage; use lightning::ln::PaymentSecret; @@ -201,47 +200,29 @@ impl Node { match channel_msg { ChannelMessage::Offer(offer) => { - let action = decide_subchannel_offer_action( - OffsetDateTime::from_unix_timestamp( - offer.contract_info.get_closest_maturity_date() as i64, - ) - .expect("A contract should always have a valid maturity timestamp."), + tracing::info!( + channel_id = offer.temporary_channel_id.to_hex(), + "Automatically accepting dlc channel offer" ); - self.process_dlc_channel_offer(offer.temporary_channel_id, action)?; + self.process_dlc_channel_offer(&offer.temporary_channel_id)?; } ChannelMessage::SettleOffer(offer) => { - self.inner - .accept_dlc_channel_collaborative_settlement(&offer.channel_id) - .with_context(|| { - format!( - "Failed to accept DLC channel close offer for channel {}", - hex::encode(offer.channel_id) - ) - })?; + tracing::info!( + channel_id = offer.channel_id.to_hex(), + "Automatically accepting settle offer" + ); + self.process_settle_offer(&offer.channel_id)?; } ChannelMessage::RenewOffer(r) => { - let channel_id_hex = r.channel_id.to_hex(); - tracing::info!( - channel_id = %channel_id_hex, + channel_id = r.channel_id.to_hex(), "Automatically accepting renew offer" ); - // TODO: This is generally not safe. The coordinator will even send - // `RenewOffer`s in order to roll over, and these will not even be triggered - // by a user action. - let (accept_renew_offer, counterparty_pubkey) = - self.inner.dlc_manager.accept_renew_offer(&r.channel_id)?; - - self.send_dlc_message( - counterparty_pubkey, - Message::Channel(ChannelMessage::RenewAccept(accept_renew_offer)), - )?; - let expiry_timestamp = OffsetDateTime::from_unix_timestamp( r.contract_info.get_closest_maturity_date() as i64, )?; - position::handler::handle_channel_renewal_offer(expiry_timestamp)?; + self.process_renew_offer(&r.channel_id, expiry_timestamp)?; } ChannelMessage::RenewRevoke(r) => { let channel_id_hex = r.channel_id.to_hex(); @@ -253,7 +234,7 @@ impl Node { let expiry_timestamp = self .inner - .get_expiry_for_confirmed_dlc_channel(r.channel_id)?; + .get_expiry_for_confirmed_dlc_channel(&r.channel_id)?; match db::get_order_in_filling()? { Some(_) => { @@ -284,7 +265,7 @@ impl Node { ChannelMessage::Sign(signed) => { let expiry_timestamp = self .inner - .get_expiry_for_confirmed_dlc_channel(signed.channel_id)?; + .get_expiry_for_confirmed_dlc_channel(&signed.channel_id)?; let filled_order = order::handler::order_filled() .context("Cannot mark order as filled for confirmed DLC")?; @@ -350,57 +331,47 @@ impl Node { } #[instrument(fields(channel_id = channel_id.to_hex()),skip_all, err(Debug))] - fn process_dlc_channel_offer( - &self, - channel_id: DlcChannelId, - action: SubchannelOfferAction, - ) -> Result<()> { - OffsetDateTime::now_utc(); + pub fn reject_dlc_channel_offer(&self, channel_id: &DlcChannelId) -> Result<()> { + tracing::warn!("Rejecting dlc channel offer!"); - match &action { - SubchannelOfferAction::Accept => { - if let Err(error) = self - .inner - .accept_dlc_channel_offer(&channel_id) - .with_context(|| { - format!( - "Failed to accept DLC channel offer for channel {}", - hex::encode(channel_id) - ) - }) - { - tracing::error!( - "Could not accept dlc channel offer {error:#}. Continuing with rejecting it" - ); - self.process_dlc_channel_offer(channel_id, SubchannelOfferAction::RejectError)? - } - } - SubchannelOfferAction::RejectOutdated | SubchannelOfferAction::RejectError => { - let (invalid_offer_reason, invalid_offer_msg) = - if let SubchannelOfferAction::RejectOutdated = action { - let invalid_offer_msg = "Offer outdated, rejecting subchannel offer"; - tracing::warn!(invalid_offer_msg); - (InvalidSubchannelOffer::Outdated, invalid_offer_msg) - } else { - let invalid_offer_msg = "Offer is unacceptable, rejecting subchannel offer"; - tracing::warn!(invalid_offer_msg); - (InvalidSubchannelOffer::Unacceptable, invalid_offer_msg) - }; - self.inner - .reject_dlc_channel_offer(&channel_id) - .with_context(|| { - format!( - "Failed to reject DLC channel offer for channel {}", - hex::encode(channel_id) - ) - })?; - - order::handler::order_failed( - None, - FailureReason::InvalidDlcOffer(invalid_offer_reason), - anyhow!(invalid_offer_msg), + let (reject, counterparty) = self + .inner + .dlc_manager + .reject_channel(channel_id) + .with_context(|| { + format!( + "Failed to reject DLC channel offer for channel {}", + hex::encode(channel_id) ) - .context("Could not set order to failed")?; + })?; + + order::handler::order_failed( + None, + FailureReason::InvalidDlcOffer(InvalidSubchannelOffer::Unacceptable), + anyhow!("Failed to accept dlc channel offer"), + ) + .context("Could not set order to failed")?; + + self.send_dlc_message( + counterparty, + Message::Channel(ChannelMessage::Reject(reject)), + ) + } + + #[instrument(fields(channel_id = channel_id.to_hex()),skip_all, err(Debug))] + pub fn process_dlc_channel_offer(&self, channel_id: &DlcChannelId) -> Result<()> { + // TODO(holzeis): We should check if the offered amounts are expected. + + match self.inner.dlc_manager.accept_channel(channel_id) { + Ok((accept_channel, _, _, node_id)) => { + self.send_dlc_message( + node_id, + Message::Channel(ChannelMessage::Accept(accept_channel)), + )?; + } + Err(e) => { + tracing::error!("Failed to accept dlc channel offer. {e:#}"); + self.reject_dlc_channel_offer(channel_id)?; } } @@ -408,59 +379,78 @@ impl Node { } #[instrument(fields(channel_id = channel_id.to_hex()),skip_all, err(Debug))] - pub fn process_subchannel_offer( + pub fn reject_settle_offer(&self, channel_id: &DlcChannelId) -> Result<()> { + tracing::warn!("Rejecting pending dlc channel collaborative settlement offer!"); + let (reject, counterparty) = self.inner.dlc_manager.reject_settle_offer(channel_id)?; + + order::handler::order_failed( + None, + FailureReason::InvalidDlcOffer(InvalidSubchannelOffer::Unacceptable), + anyhow!("Failed to accept settle offer"), + )?; + + self.send_dlc_message( + counterparty, + Message::Channel(ChannelMessage::Reject(reject)), + ) + } + + #[instrument(fields(channel_id = channel_id.to_hex()),skip_all, err(Debug))] + pub fn process_settle_offer(&self, channel_id: &DlcChannelId) -> Result<()> { + // TODO(holzeis): We should check if the offered amounts are expected. + + if let Err(e) = self + .inner + .accept_dlc_channel_collaborative_settlement(channel_id) + { + tracing::error!("Failed to accept dlc channel collaborative settlement offer. {e:#}"); + self.reject_settle_offer(channel_id)?; + } + + Ok(()) + } + + #[instrument(fields(channel_id = channel_id.to_hex()),skip_all, err(Debug))] + pub fn reject_renew_offer(&self, channel_id: &DlcChannelId) -> Result<()> { + tracing::warn!("Rejecting dlc channel renew offer!"); + + let (reject, counterparty) = self.inner.dlc_manager.reject_renew_offer(channel_id)?; + + order::handler::order_failed( + None, + FailureReason::InvalidDlcOffer(InvalidSubchannelOffer::Unacceptable), + anyhow!("Failed to accept renew offer"), + )?; + + self.send_dlc_message( + counterparty, + Message::Channel(ChannelMessage::Reject(reject)), + ) + } + + #[instrument(fields(channel_id = channel_id.to_hex()),skip_all, err(Debug))] + pub fn process_renew_offer( &self, - channel_id: ChannelId, - action: SubchannelOfferAction, + channel_id: &DlcChannelId, + expiry_timestamp: OffsetDateTime, ) -> Result<()> { - OffsetDateTime::now_utc(); + // TODO(holzeis): We should check if the offered amounts are expected. - match &action { - SubchannelOfferAction::Accept => { - if let Err(error) = self - .inner - .accept_sub_channel_offer(&channel_id) - .with_context(|| { - format!( - "Failed to accept DLC channel offer for channel {}", - hex::encode(channel_id.0) - ) - }) - { - tracing::error!( - "Could not accept subchannel offer {error:#}. Continuing with rejecting it" - ); - self.process_subchannel_offer(channel_id, SubchannelOfferAction::RejectError)? - } + match self.inner.dlc_manager.accept_renew_offer(channel_id) { + Ok((renew_accept, node_id)) => { + position::handler::handle_channel_renewal_offer(expiry_timestamp)?; + + self.send_dlc_message( + node_id, + Message::Channel(ChannelMessage::RenewAccept(renew_accept)), + )?; } - SubchannelOfferAction::RejectOutdated | SubchannelOfferAction::RejectError => { - let (invalid_offer_reason, invalid_offer_msg) = - if let SubchannelOfferAction::RejectOutdated = action { - let invalid_offer_msg = "Offer outdated, rejecting subchannel offer"; - tracing::warn!(invalid_offer_msg); - (InvalidSubchannelOffer::Outdated, invalid_offer_msg) - } else { - let invalid_offer_msg = "Offer is unacceptable, rejecting subchannel offer"; - tracing::warn!(invalid_offer_msg); - (InvalidSubchannelOffer::Unacceptable, invalid_offer_msg) - }; - self.inner - .reject_sub_channel_offer(&channel_id) - .with_context(|| { - format!( - "Failed to reject DLC channel offer for channel {}", - hex::encode(channel_id.0) - ) - })?; + Err(e) => { + tracing::error!("Failed to accept dlc channel renew offer. {e:#}"); - order::handler::order_failed( - None, - FailureReason::InvalidDlcOffer(invalid_offer_reason), - anyhow!(invalid_offer_msg), - ) - .context("Could not set order to failed")?; + self.reject_renew_offer(channel_id)?; } - } + }; Ok(()) } @@ -511,26 +501,6 @@ impl Node { } } -pub(crate) fn decide_subchannel_offer_action( - maturity_timestamp: OffsetDateTime, -) -> SubchannelOfferAction { - let mut action = SubchannelOfferAction::Accept; - if OffsetDateTime::now_utc().gt(&maturity_timestamp) { - action = SubchannelOfferAction::RejectOutdated; - } - action -} - -pub enum SubchannelOfferAction { - Accept, - /// The offer was outdated, hence we need to reject the offer - RejectOutdated, - // /// The offer was invalid, hence we need to reject the offer - // RejectInvalid, - /// We had an error accepting, hence, we try to reject instead - RejectError, -} - #[derive(Clone)] pub struct NodeStorage; diff --git a/mobile/native/src/logger.rs b/mobile/native/src/logger.rs index be1bd2c80..a7ad03423 100644 --- a/mobile/native/src/logger.rs +++ b/mobile/native/src/logger.rs @@ -4,6 +4,7 @@ use flutter_rust_bridge::StreamSink; use std::collections::BTreeMap; use std::sync::Arc; use std::sync::Once; +use tracing_log::LogTracer; use tracing_subscriber::filter::Directive; use tracing_subscriber::filter::LevelFilter; use tracing_subscriber::fmt::time; @@ -168,6 +169,8 @@ pub fn init_tracing(level: LevelFilter, json_format: bool) -> Result<()> { .try_init() .context("Failed to init tracing")?; + LogTracer::init()?; + tracing::info!("Initialized logger"); Ok(()) diff --git a/mobile/native/src/trade/order/mod.rs b/mobile/native/src/trade/order/mod.rs index 642248fe4..d72c3f965 100644 --- a/mobile/native/src/trade/order/mod.rs +++ b/mobile/native/src/trade/order/mod.rs @@ -21,7 +21,7 @@ pub enum OrderType { } /// Internal type so we still have Copy on order -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, PartialEq)] pub enum FailureReason { /// An error occurred when setting the Order to filling in our DB FailedToSetToFilling, @@ -42,7 +42,7 @@ pub enum FailureReason { Unknown, } -#[derive(Debug, Clone, Copy, Serialize)] +#[derive(Debug, Clone, Copy, Serialize, PartialEq)] pub enum InvalidSubchannelOffer { /// Received offer was outdated Outdated, @@ -50,7 +50,7 @@ pub enum InvalidSubchannelOffer { Unacceptable, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum OrderState { /// Not submitted to orderbook yet ///