Skip to content

Commit

Permalink
fix : make the channel_id inside close optional
Browse files Browse the repository at this point in the history
When there is only one channel open with the given `node_id` we
can just skip the `channel_id` from the `close` request.
  • Loading branch information
Harshit933 committed Mar 21, 2024
1 parent c012ed7 commit 4d3c1ef
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 32 deletions.
4 changes: 2 additions & 2 deletions lampo-common/src/event/ln.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub enum LightningEvent {
CloseChannelEvent {
channel_id: String,
message: String,
counterparty_node_id: String,
funding_utxo: String,
counterparty_node_id: Option<String>,
funding_utxo: Option<String>,
},
}
5 changes: 5 additions & 0 deletions lampo-common/src/model/close_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ pub mod request {
Ok(ChannelId::from_bytes(result_array))
}

/// This converts hex to bytes array.
/// Stolen from https://stackoverflow.com/a/52992629
/// It takes two values every in each iteration from the hex
/// then convert the formed hexdecimal digit to u8, collects it in a vector
/// and return it (redix = 16 for hexadecimal)
fn decode_hex(&self, s: &str) -> Result<Vec<u8>, ParseIntError> {
(0..s.len())
.step_by(2)
Expand Down
12 changes: 2 additions & 10 deletions lampod/src/actions/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,8 @@ impl Handler for LampoHandler {
channel_funding_txo,
..
} => {
let node_id = if let Some(id) = counterparty_node_id {
id.to_string()
} else {
"Node_id not found".to_string()
};
let txo = if let Some(txo) = channel_funding_txo {
txo.to_string()
} else {
"Outpoint not found".to_string()
};
let node_id = counterparty_node_id.map(|id| id.to_string());
let txo = channel_funding_txo.map(|txo| txo.to_string());
self.emit(Event::Lightning(LightningEvent::CloseChannelEvent { channel_id: channel_id.to_string(), message: reason.to_string(), counterparty_node_id : node_id, funding_utxo : txo}));
log::info!("channel `{user_channel_id}` closed with reason: `{reason}`");
Ok(())
Expand Down
57 changes: 51 additions & 6 deletions lampod/src/jsonrpc/channels.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use lampo_common::event::ln::LightningEvent;
use lampo_common::event::Event;
use lampo_common::handler::Handler;
use lampo_common::json;
use lampo_common::model::request;
use lampo_common::model::response;
use lampo_jsonrpc::errors::Error;
use lampo_jsonrpc::errors::RpcError;
use lampo_common::handler::Handler;

use crate::ln::events::ChannelEvents;

Expand All @@ -18,17 +19,61 @@ pub fn json_list_channels(ctx: &LampoDeamon, request: &json::Value) -> Result<js

pub fn json_close_channel(ctx: &LampoDeamon, request: &json::Value) -> Result<json::Value, Error> {
log::info!("call for `closechannel` with request {:?}", request);
let request: request::CloseChannel = json::from_value(request.clone())?;
let mut request: request::CloseChannel = json::from_value(request.clone())?;
let events = ctx.handler().events();
let res = ctx.channel_manager().close_channel(request);
let (message, channel_id, node_id, funding_utxo) = loop {
let event = events.recv_timeout(std::time::Duration::from_secs(30)).map_err(|err| {
let res;
// This gives all the channels with associated peer
let channels: response::Channels = ctx
.handler()
.call(
"channels",
json::json!({
"peer_id": request.node_id,
}),
)
.map_err(|err| {
Error::Rpc(RpcError {
code: -1,
message: format!("{err}"),
data: None,
})
})?;
if channels.channels.len() > 1 {
// check the channel_id if it is not none, if it is return an error
// and if it is not none then we need to have the channel_id that needs to be shut
if request.channel_id.is_none() {
return Err(Error::Rpc(RpcError {
code: -1,
message: format!("Channels > 1, provide `channel_id`"),
data: None,
}));
} else {
res = ctx.channel_manager().close_channel(request.clone());
};
} else if !channels.channels.is_empty() {
// This is the case where channel with the given node_id = 1
// SAFETY: it is safe to unwrap because the channels is not empty
let channel = channels.channels.first().unwrap();
request.channel_id = Some(channel.channel_id.clone());
res = ctx.channel_manager().close_channel(request);
} else {
// No channels with the given peer.
return Err(Error::Rpc(RpcError {
code: -1,
message: format!("No channels with associated peer"),
data: None,
}));
};
let (message, channel_id, node_id, funding_utxo) = loop {
let event = events
.recv_timeout(std::time::Duration::from_secs(30))
.map_err(|err| {
Error::Rpc(RpcError {
code: -1,
message: format!("{err}"),
data: None,
})
})?;
if let Event::Lightning(LightningEvent::CloseChannelEvent {
message,
channel_id,
Expand All @@ -38,7 +83,7 @@ pub fn json_close_channel(ctx: &LampoDeamon, request: &json::Value) -> Result<js
{
break (message, channel_id, counterparty_node_id, funding_utxo);
}
};
};
let resp = match res {
Ok(_) => Ok(json::json!({
"message" : message,
Expand Down
8 changes: 2 additions & 6 deletions lampod/src/ln/channel_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use lampo_common::bitcoin::absolute::Height;
use lampo_common::bitcoin::{BlockHash, Transaction};
use lampo_common::conf::{LampoConf, UserConfig};
use lampo_common::error;
use lampo_common::event::ln::LightningEvent;
use lampo_common::event::onchain::OnChainEvent;
use lampo_common::event::Event;
use lampo_common::handler::Handler;
Expand All @@ -32,7 +31,7 @@ use lampo_common::ldk::util::config::{ChannelHandshakeConfig, ChannelHandshakeLi
use lampo_common::ldk::util::persist::read_channel_monitors;
use lampo_common::ldk::util::ser::ReadableArgs;
use lampo_common::model::request;
use lampo_common::model::response::{self, Channel, Channels, CloseChannel};
use lampo_common::model::response::{self, Channel, Channels};

use crate::actions::handler::LampoHandler;
use crate::chain::{LampoChainManager, WalletManager};
Expand Down Expand Up @@ -450,10 +449,7 @@ impl ChannelEvents for LampoChannelManager {
})
}

fn close_channel(
&self,
channel: request::CloseChannel,
) -> error::Result<()> {
fn close_channel(&self, channel: request::CloseChannel) -> error::Result<()> {
let channel_id = channel.channel_id()?;
let node_id = channel.counterpart_node_id()?;

Expand Down
5 changes: 1 addition & 4 deletions lampod/src/ln/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ pub trait ChannelEvents {
) -> error::Result<response::OpenChannel>;

/// Close a channel
fn close_channel(
&self,
channel: request::CloseChannel,
) -> error::Result<()>;
fn close_channel(&self, channel: request::CloseChannel) -> error::Result<()>;

fn change_state_channel(&self, event: ChangeStateChannelEvent) -> error::Result<()>;
}
Expand Down
53 changes: 49 additions & 4 deletions tests/tests/src/lampo_cln_tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::str::FromStr;
use std::time::Duration;

use lampo_common::bitcoin::hashes::hex;
use lampo_common::error;
use lampo_common::event::ln::LightningEvent;
use lampo_common::event::onchain::OnChainEvent;
Expand Down Expand Up @@ -549,7 +548,6 @@ fn close_channel() {
"regtest"
))
.unwrap();
std::thread::sleep(Duration::from_secs(2));
let btc = cln.btc();
let lampo_manager = LampoTesting::new(btc.clone()).unwrap();
let lampo = lampo_manager.lampod();
Expand Down Expand Up @@ -581,7 +579,19 @@ fn close_channel() {
)
.unwrap();

std::thread::sleep(Duration::from_secs(2));
// This would be the second channel
let _: json::Value = lampo
.call(
"fundchannel",
request::OpenChannel {
node_id: cln.rpc().getinfo().unwrap().id,
port: Some(cln.port.into()),
amount: 1_000_000_000,
public: true,
addr: Some("127.0.0.1".to_owned()),
},
)
.unwrap();

// Get the transaction confirmed
let _ = btc.rpc().generate_to_address(6, &address).unwrap();
Expand Down Expand Up @@ -628,12 +638,47 @@ fn close_channel() {
// This should return the final channel_id as channel_id may differ from the time being in ChannelPending, ChannelClosed state.
let channels: response::Channels = lampo.call("channels", json::json!({})).unwrap();

// This should fail as there are two channels with the peer so we need to pass the specific `channel_id`
let result: Result<response::CloseChannel, _> = lampo.call(
"close",
request::CloseChannel {
node_id: info_cln.id.to_string(),
channel_id: Some(channels.channels.first().unwrap().channel_id),
channel_id: None,
},
);

assert!(result.is_err(), "{:?}", result);

// Closing the first channel
let result: Result<response::CloseChannel, _> = lampo.call(
"close",
request::CloseChannel {
node_id: info_cln.id.to_string(),
channel_id: Some(channels.channels.first().unwrap().channel_id.to_string()),
},
);
assert!(result.is_ok(), "{:?}", result);
// assert_eq!(&result.unwrap().counterparty_node_id, &info_cln.id.to_string());
assert_eq!(&result.unwrap().channel_id, &channels.channels.first().unwrap().channel_id.to_string());

// Closing the second channel - at this point there is only 1 channel with the peer
let result: Result<response::CloseChannel, _> = lampo.call(
"close",
request::CloseChannel {
node_id: info_cln.id.to_string(),
channel_id: None,
},
);
assert!(result.is_ok(), "{:?}", result);
assert_eq!(result.unwrap().counterparty_node_id, info_cln.id.to_string());

// Closing the third channel (this channel does not exist)
let result: Result<response::CloseChannel, _> = lampo.call(
"close",
request::CloseChannel {
node_id: info_cln.id.to_string(),
channel_id: Some(channels.channels.first().unwrap().channel_id.to_string()),
},
);
assert!(result.is_err(), "{:?}", result);
}

0 comments on commit 4d3c1ef

Please sign in to comment.