Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat : close channel command #198

Merged
merged 4 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lampo-common/src/event/ln.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,10 @@ pub enum LightningEvent {
state: ChannelState,
message: String,
},
CloseChannelEvent {
channel_id: String,
message: String,
counterparty_node_id: Option<String>,
funding_utxo: Option<String>,
},
}
3 changes: 3 additions & 0 deletions lampo-common/src/model.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod close_channel;
mod connect;
mod getinfo;
mod invoice;
Expand All @@ -10,6 +11,7 @@ pub use connect::Connect;
pub use getinfo::GetInfo;

pub mod request {
pub use crate::model::close_channel::request::*;
pub use crate::model::connect::Connect;
pub use crate::model::getinfo::GetInfo;
pub use crate::model::invoice::request::*;
Expand All @@ -21,6 +23,7 @@ pub mod request {
}

pub mod response {
pub use crate::model::close_channel::response::*;
pub use crate::model::connect::Connect;
pub use crate::model::getinfo::GetInfo;
pub use crate::model::invoice::response::*;
Expand Down
60 changes: 60 additions & 0 deletions lampo-common/src/model/close_channel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
pub mod request {
use std::num::ParseIntError;
use std::str::FromStr;

use bitcoin::secp256k1::PublicKey;
use serde::{Deserialize, Serialize};

use crate::error;
use crate::types::*;

#[derive(Clone, Serialize, Deserialize)]
pub struct CloseChannel {
pub node_id: String,
// Hex of the channel
pub channel_id: Option<String>,
}

impl CloseChannel {
pub fn counterpart_node_id(&self) -> error::Result<PublicKey> {
let node_id = PublicKey::from_str(&self.node_id)?;
Ok(node_id)
}

// Returns ChannelId in byte format from hex of channelid
pub fn channel_id(&self) -> error::Result<ChannelId> {
let id = self
.channel_id
.as_ref()
.ok_or(error::anyhow!("`channel_id` not found"))?;
let result = self.decode_hex(&id)?;
let mut result_array: [u8; 32] = [0; 32];
result_array.copy_from_slice(&result);
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)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16))
.collect()
}
}
}

pub mod response {
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct CloseChannel {
pub channel_id: String,
pub message: String,
pub peer_id: String,
pub funding_utxo: String,
}
}
4 changes: 3 additions & 1 deletion lampo-common/src/model/open_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub mod response {
use crate::error;
use crate::types::NodeId;

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone)]
pub struct Channels {
pub channels: Vec<Channel>,
}
Expand All @@ -58,6 +58,8 @@ pub mod response {

#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Channel {
// Channel_id needs to be string as it currently does not derive Serialize
pub channel_id: String,
pub short_channel_id: Option<u64>,
pub peer_id: String,
pub peer_alias: Option<String>,
Expand Down
2 changes: 2 additions & 0 deletions lampo-testing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use clightning_testing::prelude::*;
use lampo_common::json;
use lampo_common::model::response;
use lampo_common::model::response::NewAddress;
use lampod::jsonrpc::channels::json_close_channel;
use lampod::jsonrpc::offchain::json_keysend;
use tempfile::TempDir;

Expand Down Expand Up @@ -117,6 +118,7 @@ impl LampoTesting {

server.add_rpc("pay", json_pay).unwrap();
server.add_rpc("keysend", json_keysend).unwrap();
server.add_rpc("close", json_close_channel).unwrap();
let handler = server.handler();
let rpc_handler = Arc::new(CommandHandler::new(&lampo_conf)?);
rpc_handler.set_handler(handler);
Expand Down
2 changes: 2 additions & 0 deletions lampod-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::str::FromStr;
use std::sync::Arc;
use std::thread::JoinHandle;

use lampod::jsonrpc::channels::json_close_channel;
use lampod::jsonrpc::channels::json_list_channels;

use lampo_bitcoind::BitcoinCore;
Expand Down Expand Up @@ -171,6 +172,7 @@ fn run_jsonrpc(
server.add_rpc("pay", json_pay).unwrap();
server.add_rpc("keysend", json_keysend).unwrap();
server.add_rpc("fees", json_estimate_fees).unwrap();
server.add_rpc("close", json_close_channel).unwrap();
let handler = server.handler();
Ok((server.spawn(), handler))
}
7 changes: 6 additions & 1 deletion lampod/src/actions/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,18 @@ impl Handler for LampoHandler {
channel_type,
}));
Ok(())
}
},
ldk::events::Event::ChannelClosed {
channel_id,
user_channel_id,
reason,
counterparty_node_id,
channel_funding_txo,
..
} => {
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
92 changes: 92 additions & 0 deletions lampod/src/jsonrpc/channels.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
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 crate::ln::events::ChannelEvents;

use crate::LampoDeamon;

Expand All @@ -8,3 +16,87 @@ pub fn json_list_channels(ctx: &LampoDeamon, request: &json::Value) -> Result<js
let resp = ctx.channel_manager().list_channel();
Ok(json::to_value(resp)?)
}

pub fn json_close_channel(ctx: &LampoDeamon, request: &json::Value) -> Result<json::Value, Error> {
log::info!("call for `closechannel` with request {:?}", request);
let mut request: request::CloseChannel = json::from_value(request.clone())?;
let events = ctx.handler().events();
// 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,
})
})?;
let res = 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 {
request
}
} 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());
request
} else {
// No channels with the given peer.
return Err(Error::Rpc(RpcError {
code: -1,
message: format!("No channels with associated peer"),
data: None,
}));
};
match ctx.channel_manager().close_channel(res) {
Err(err) => {
return Err(Error::Rpc(RpcError {
code: -1,
message: format!("{err}"),
data: None,
}))
}
Ok(_) => {}
};
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,
counterparty_node_id,
funding_utxo,
}) = event
{
break (message, channel_id, counterparty_node_id, funding_utxo);
}
};
Ok(json::json!({
"message" : message,
"channel_id" : channel_id,
"peer_id" : node_id,
"funding_utxo" : funding_utxo,
}))
}
12 changes: 9 additions & 3 deletions lampod/src/ln/channel_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ impl LampoChannelManager {
.list_channels()
.into_iter()
.map(|channel| Channel {
channel_id: channel.channel_id.to_string(),
short_channel_id: channel.short_channel_id,
peer_id: channel.counterparty.node_id.to_string(),
peer_alias: None,
Expand Down Expand Up @@ -448,10 +449,15 @@ impl ChannelEvents for LampoChannelManager {
})
}

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

self.manager()
.close_channel(&channel_id, &node_id)
.map_err(|err| error::anyhow!("{:?}", err))?;
Ok(())
}
fn change_state_channel(&self, _: ChangeStateChannelEvent) -> error::Result<()> {
unimplemented!()
}
Expand Down
2 changes: 1 addition & 1 deletion lampod/src/ln/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub trait ChannelEvents {
) -> error::Result<response::OpenChannel>;

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

fn change_state_channel(&self, event: ChangeStateChannelEvent) -> error::Result<()>;
}
Expand Down
Loading
Loading