clndash: initial peer channel listing
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -3072,9 +3072,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lnsocket"
|
name = "lnsocket"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a88bd51e5bb3753f89b0d3e73baa565064c5a9f5b2aad3ab3f3db5fffb89955"
|
checksum = "6a373bcde8b65d6db11a0cd0f70dd4a24af854dd7a112b0a51258593c65f48ff"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
"hashbrown 0.13.2",
|
"hashbrown 0.13.2",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ egui = { workspace = true }
|
|||||||
notedeck = { workspace = true }
|
notedeck = { workspace = true }
|
||||||
#notedeck_ui = { workspace = true }
|
#notedeck_ui = { workspace = true }
|
||||||
eframe = { workspace = true }
|
eframe = { workspace = true }
|
||||||
lnsocket = "0.3.0"
|
lnsocket = "0.4.0"
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
|||||||
@@ -3,14 +3,19 @@ use lnsocket::bitcoin::secp256k1::{PublicKey, SecretKey, rand};
|
|||||||
use lnsocket::{CommandoClient, LNSocket};
|
use lnsocket::{CommandoClient, LNSocket};
|
||||||
use notedeck::{AppAction, AppContext};
|
use notedeck::{AppAction, AppContext};
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel};
|
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel};
|
||||||
|
|
||||||
|
type JsonCache = HashMap<String, String>;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ClnDash {
|
pub struct ClnDash {
|
||||||
initialized: bool,
|
initialized: bool,
|
||||||
connection_state: ConnectionState,
|
connection_state: ConnectionState,
|
||||||
get_info: Option<String>,
|
get_info: Option<String>,
|
||||||
|
peer_channels: Option<Vec<Value>>,
|
||||||
|
json_cache: JsonCache,
|
||||||
channel: Option<Channel>,
|
channel: Option<Channel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +32,8 @@ struct Channel {
|
|||||||
|
|
||||||
/// Responses from the socket
|
/// Responses from the socket
|
||||||
enum ClnResponse {
|
enum ClnResponse {
|
||||||
GetInfo(Result<Value, String>),
|
GetInfo(Value),
|
||||||
|
ListPeerChannels(Value),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConnectionState {
|
enum ConnectionState {
|
||||||
@@ -36,8 +42,10 @@ enum ConnectionState {
|
|||||||
Active,
|
Active,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||||
enum Request {
|
enum Request {
|
||||||
GetInfo,
|
GetInfo,
|
||||||
|
ListPeerChannels,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Event {
|
enum Event {
|
||||||
@@ -92,11 +100,15 @@ impl ClnDash {
|
|||||||
egui::Frame::new()
|
egui::Frame::new()
|
||||||
.inner_margin(egui::Margin::same(50))
|
.inner_margin(egui::Margin::same(50))
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
connection_state_ui(ui, &self.connection_state);
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
connection_state_ui(ui, &self.connection_state);
|
||||||
|
|
||||||
if let Some(info) = self.get_info.as_ref() {
|
channels_ui(ui, &mut self.json_cache, &self.peer_channels);
|
||||||
get_info_ui(ui, info);
|
|
||||||
}
|
if let Some(info) = self.get_info.as_ref() {
|
||||||
|
get_info_ui(ui, info);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +139,10 @@ impl ClnDash {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let rune = "Vns1Zxvidr4J8pP2ZCg3Wjp2SyGyyf5RHgvFG8L36yM9MzMmbWV0aG9kPWdldGluZm8="; // getinfo only atm
|
let rune = std::env::var("RUNE").unwrap_or(
|
||||||
let commando = CommandoClient::spawn(lnsocket, rune);
|
"Vns1Zxvidr4J8pP2ZCg3Wjp2SyGyyf5RHgvFG8L36yM9MzMmbWV0aG9kPWdldGluZm8=".to_string(),
|
||||||
|
);
|
||||||
|
let commando = CommandoClient::spawn(lnsocket, &rune);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match req_rx.recv().await {
|
match req_rx.recv().await {
|
||||||
@@ -139,18 +153,32 @@ impl ClnDash {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(req) => match req {
|
Some(req) => {
|
||||||
Request::GetInfo => match commando.call("getinfo", json!({})).await {
|
tracing::debug!("calling {req:?}");
|
||||||
Ok(v) => {
|
match req {
|
||||||
let _ = event_tx.send(Event::Response(ClnResponse::GetInfo(Ok(v))));
|
Request::GetInfo => match commando.call("getinfo", json!({})).await {
|
||||||
|
Ok(v) => {
|
||||||
|
let _ = event_tx.send(Event::Response(ClnResponse::GetInfo(v)));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
tracing::error!("get_info error {}", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Request::ListPeerChannels => {
|
||||||
|
match commando.call("listpeerchannels", json!({})).await {
|
||||||
|
Ok(v) => {
|
||||||
|
let _ = event_tx.send(Event::Response(
|
||||||
|
ClnResponse::ListPeerChannels(v),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
tracing::error!("listpeerchannels error {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
}
|
||||||
let _ = event_tx.send(Event::Ended {
|
}
|
||||||
reason: err.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -170,14 +198,17 @@ impl ClnDash {
|
|||||||
Event::Connected => {
|
Event::Connected => {
|
||||||
self.connection_state = ConnectionState::Active;
|
self.connection_state = ConnectionState::Active;
|
||||||
let _ = channel.req_tx.send(Request::GetInfo);
|
let _ = channel.req_tx.send(Request::GetInfo);
|
||||||
|
let _ = channel.req_tx.send(Request::ListPeerChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Response(resp) => match resp {
|
Event::Response(resp) => match resp {
|
||||||
ClnResponse::GetInfo(value) => {
|
ClnResponse::ListPeerChannels(chans) => {
|
||||||
let Ok(value) = value else {
|
if let Some(vs) = chans["channels"].as_array() {
|
||||||
return;
|
self.peer_channels = Some(vs.to_owned());
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClnResponse::GetInfo(value) => {
|
||||||
if let Ok(s) = serde_json::to_string_pretty(&value) {
|
if let Ok(s) = serde_json::to_string_pretty(&value) {
|
||||||
self.get_info = Some(s);
|
self.get_info = Some(s);
|
||||||
}
|
}
|
||||||
@@ -193,3 +224,28 @@ fn get_info_ui(ui: &mut egui::Ui, info: &str) {
|
|||||||
ui.add(Label::new(info).wrap_mode(egui::TextWrapMode::Wrap));
|
ui.add(Label::new(info).wrap_mode(egui::TextWrapMode::Wrap));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn channel_ui(ui: &mut egui::Ui, cache: &mut JsonCache, channel: &Value) {
|
||||||
|
let short_channel_id = channel["short_channel_id"].as_str().unwrap_or("??");
|
||||||
|
|
||||||
|
egui::CollapsingHeader::new(format!("channel {short_channel_id}"))
|
||||||
|
.id_salt(("section", short_channel_id))
|
||||||
|
.show(ui, |ui| {
|
||||||
|
let json: &String = cache
|
||||||
|
.entry(short_channel_id.to_owned())
|
||||||
|
.or_insert_with(|| serde_json::to_string_pretty(channel).unwrap());
|
||||||
|
|
||||||
|
ui.add(Label::new(json).wrap_mode(egui::TextWrapMode::Wrap));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn channels_ui(ui: &mut egui::Ui, json_cache: &mut JsonCache, channels: &Option<Vec<Value>>) {
|
||||||
|
let Some(channels) = channels else {
|
||||||
|
ui.label("no channels");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for channel in channels {
|
||||||
|
channel_ui(ui, json_cache, channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user