Skip to content

Commit

Permalink
Merge pull request #1099 from get10101/feature/acknowledge-force-clos…
Browse files Browse the repository at this point in the history
…e-in-app

Display channel status in app
  • Loading branch information
luckysori authored Aug 18, 2023
2 parents 0dfe631 + 73e3a3a commit 3de0fe3
Show file tree
Hide file tree
Showing 18 changed files with 455 additions and 56 deletions.
30 changes: 24 additions & 6 deletions crates/ln-dlc-node/src/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ use uuid::Uuid;
/// reporting purposes.
#[derive(Debug, Clone, PartialEq)]
pub struct Channel {
/// The `user_channel_id` is set by 10101 at the time the `Event::HTLCIntercepted` when
/// we are attempting to create a JIT channel.
/// Custom identifier for a channel which is generated outside of LDK.
///
/// The coordinator sets it after receiving `Event::HTLCIntercepted` as a result of trying to
/// create a JIT channel.
///
/// The app sets its own when calling `accept_inbound_channel_from_trusted_peer_0conf` when
/// accepting an inbound JIT channel from the coordinator.
pub user_channel_id: UserChannelId,
/// Until the `Event::ChannelReady` we do not have a `channel_id`, which is derived from
/// the funding transaction. We use the `user_channel_id` as identifier over the entirety
Expand All @@ -45,13 +50,14 @@ pub struct Channel {

impl Channel {
pub fn new(
user_channel_id: UserChannelId,
inbound: u64,
outbound: u64,
counterparty: PublicKey,
intercept_id: Option<FakeScid>,
) -> Self {
Channel {
user_channel_id: UserChannelId::new(),
user_channel_id,
channel_state: ChannelState::Pending,
inbound,
outbound,
Expand Down Expand Up @@ -123,10 +129,18 @@ impl Channel {
let mut channel = match channel {
Some(channel) => channel,
None => {
let user_channel_id =
UserChannelId::from(channel_details.user_channel_id).to_string();
tracing::warn!(%user_channel_id, channel_id = %channel_details.channel_id.to_hex(), public = channel_details.is_public, outbound = channel_details.is_outbound, "Cannot open non-existent shadow channel. Creating a new one.");
let user_channel_id = UserChannelId::from(channel_details.user_channel_id);

tracing::info!(
user_channel_id = %user_channel_id.to_string(),
channel_id = %channel_details.channel_id.to_hex(),
public = channel_details.is_public,
outbound = channel_details.is_outbound,
"Cannot open non-existent shadow channel. Creating a new one."
);

Channel::new(
user_channel_id,
channel_details.inbound_capacity_msat,
0,
channel_details.counterparty.node_id,
Expand All @@ -153,6 +167,10 @@ impl Channel {
#[derive(PartialEq, Debug, Clone)]
pub enum ChannelState {
/// Corresponds to a JIT channel which an app user has registered interest in opening.
///
/// TODO: This enum is shared between all consumers, but this variant is only used by the
/// coordinator. This is problematic because all other consumers still have to handle this
/// unreachable variant.
Announced,
Pending,
Open,
Expand Down
4 changes: 3 additions & 1 deletion crates/ln-dlc-node/src/ln/app_event_handler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::common_handlers;
use super::event_handler::EventSender;
use super::event_handler::PendingInterceptedHtlcs;
use crate::channel::UserChannelId;
use crate::node::ChannelManager;
use crate::node::Node;
use crate::node::Storage;
Expand Down Expand Up @@ -226,11 +227,12 @@ pub(crate) fn handle_open_channel_request_0_conf(
push_msat,
"Accepting open channel request"
);

channel_manager
.accept_inbound_channel_from_trusted_peer_0conf(
&temporary_channel_id,
&counterparty_node_id,
0,
UserChannelId::new().to_u128(),
)
.map_err(|e| anyhow!("{e:?}"))
.context("To be able to accept a 0-conf channel")?;
Expand Down
12 changes: 10 additions & 2 deletions crates/ln-dlc-node/src/ln/common_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,16 @@ where
}
}

node.sub_channel_manager
.notify_ln_channel_closed(channel_id)?;
match node
.sub_channel_manager
.notify_ln_channel_closed(channel_id)
{
Ok(()) => {}
Err(dlc_manager::error::Error::InvalidParameters(msg)) => {
tracing::debug!("Irrelevant LDK closure notification: {msg}");
}
e @ Err(_) => e.context("Failed to notify subchannel manager about LDK closure")?,
};

anyhow::Ok(())
})?;
Expand Down
4 changes: 0 additions & 4 deletions crates/tests-e2e/examples/fund.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ async fn main() {
}

async fn fund_everything(faucet: &str, coordinator: &str) -> Result<()> {
// let node_info = get_node_info(faucet).await?;
// dbg!(node_info);
// return Ok(());

let coordinator = Coordinator::new(init_reqwest(), coordinator);
let coord_addr = coordinator.get_new_address().await?;
fund(&coord_addr, Amount::ONE_BTC, faucet).await?;
Expand Down
14 changes: 14 additions & 0 deletions crates/tests-e2e/src/test_subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use native::event::EventType;
use native::health::Service;
use native::health::ServiceStatus;
use native::health::ServiceUpdate;
use native::ln_dlc::ChannelStatus;
use native::trade::order::Order;
use native::trade::position::Position;
use orderbook_commons::Prices;
Expand All @@ -24,6 +25,7 @@ pub struct Senders {
prices: watch::Sender<Option<Prices>>,
position_close: watch::Sender<Option<ContractSymbol>>,
service: watch::Sender<Option<ServiceUpdate>>,
channel_status: watch::Sender<Option<ChannelStatus>>,
}

/// Subscribes to events destined for the frontend (typically Flutter app) and
Expand All @@ -37,6 +39,7 @@ pub struct TestSubscriber {
prices: watch::Receiver<Option<Prices>>,
position_close: watch::Receiver<Option<ContractSymbol>>,
services: Arc<Mutex<HashMap<Service, ServiceStatus>>>,
channel_status: watch::Receiver<Option<ChannelStatus>>,
_service_map_updater: tokio::task::JoinHandle<()>,
}

Expand All @@ -50,6 +53,7 @@ impl TestSubscriber {
let (prices_tx, prices_rx) = watch::channel(None);
let (position_close_tx, position_close_rx) = watch::channel(None);
let (service_tx, mut service_rx) = watch::channel(None);
let (channel_status_tx, channel_status_rx) = watch::channel(None);

let senders = Senders {
wallet_info: wallet_info_tx,
Expand All @@ -60,6 +64,7 @@ impl TestSubscriber {
prices: prices_tx,
position_close: position_close_tx,
service: service_tx,
channel_status: channel_status_tx,
};

let services = Arc::new(Mutex::new(HashMap::new()));
Expand Down Expand Up @@ -89,6 +94,7 @@ impl TestSubscriber {
prices: prices_rx,
position_close: position_close_rx,
services,
channel_status: channel_status_rx,
_service_map_updater,
};
(subscriber, ThreadSafeSenders(Arc::new(Mutex::new(senders))))
Expand Down Expand Up @@ -130,6 +136,10 @@ impl TestSubscriber {
.copied()
.unwrap_or_default()
}

pub fn channel_status(&self) -> Option<ChannelStatus> {
self.channel_status.borrow().as_ref().cloned()
}
}

impl Subscriber for Senders {
Expand All @@ -148,6 +158,7 @@ impl Subscriber for Senders {
EventType::PositionClosedNotification,
EventType::PriceUpdateNotification,
EventType::ServiceHealthUpdate,
EventType::ChannelStatusUpdate,
]
}
}
Expand Down Expand Up @@ -183,6 +194,9 @@ impl Senders {
native::event::EventInternal::ServiceHealthUpdate(update) => {
self.service.send(Some(update.clone()))?;
}
native::event::EventInternal::ChannelStatusUpdate(update) => {
self.channel_status.send(Some(*update))?;
}
native::event::EventInternal::ChannelReady(_channel_id) => {
unreachable!("ChannelReady event should not be sent to the subscriber");
}
Expand Down
8 changes: 7 additions & 1 deletion mobile/lib/common/app_bar_wrapper.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:get_10101/common/channel_status_notifier.dart';
import 'package:get_10101/common/color.dart';
import 'package:get_10101/common/settings_screen.dart';
import 'package:get_10101/features/trade/trade_screen.dart';
import 'package:get_10101/features/wallet/wallet_screen.dart';
import 'package:get_10101/features/wallet/status_screen.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';

class AppBarWrapper extends StatelessWidget {
const AppBarWrapper({
Expand All @@ -16,9 +18,13 @@ class AppBarWrapper extends StatelessWidget {
final currentRoute = GoRouterState.of(context).location;
const appBarHeight = 35.0;

ChannelStatusNotifier channelStatusNotifier = context.watch<ChannelStatusNotifier>();

var actionButtons = [
IconButton(
icon: const Icon(Icons.thermostat),
icon: channelStatusNotifier.isClosing()
? const Icon(Icons.thermostat, color: Colors.red)
: const Icon(Icons.thermostat),
tooltip: 'Status',
onPressed: () {
context.go(WalletStatusScreen.route);
Expand Down
46 changes: 46 additions & 0 deletions mobile/lib/common/channel_status_notifier.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'package:f_logs/f_logs.dart';
import 'package:flutter/material.dart';
import 'package:get_10101/bridge_generated/bridge_definitions.dart' as bridge;
import 'package:get_10101/common/application/event_service.dart';
import 'package:get_10101/ffi.dart';

/// Sends channel status notifications to subscribers.
///
/// Subscribers can learn about the latest [bridge.ChannelStatus] of the LN-DLC channel.
class ChannelStatusNotifier extends ChangeNotifier implements Subscriber {
bridge.ChannelStatus latest = ChannelStatus.Unknown;

ChannelStatusNotifier();

/// Get the latest status of the LN-DLC channel.
bridge.ChannelStatus getChannelStatus() {
return latest;
}

/// Whether the current LN-DLC channel is closed or not.
bool isClosing() {
final status = getChannelStatus();

return status == ChannelStatus.LnDlcForceClosing;
}

void subscribe(EventService eventService) {
eventService.subscribe(this, const bridge.Event.channelStatusUpdate(ChannelStatus.Unknown));
}

@override

/// Handle events coming from the Rust backend.
///
/// We only care about [bridge.Event_ChannelStatusUpdate], as they pertain to
/// the channel status. If we get a relevant event we update our state and
/// notify all listeners.
void notify(bridge.Event event) {
if (event is bridge.Event_ChannelStatusUpdate) {
FLog.debug(text: "Received channel status update: ${event.toString()}");
latest = event.field0;

notifyListeners();
}
}
}
3 changes: 2 additions & 1 deletion mobile/lib/common/service_status_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ class ServiceStatusNotifier extends ChangeNotifier implements Subscriber {
FLog.debug(text: "Received event: ${event.toString()}");
var update = event.field0;
services[update.service] = update.status;

notifyListeners();
} else {
FLog.warning(text: "Received unexpected event: ${event.toString()}");
}
notifyListeners();
}
}

Expand Down
Loading

0 comments on commit 3de0fe3

Please sign in to comment.