diff --git a/Cargo.lock b/Cargo.lock index 454789f..fb8dc15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "amplify_derive" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4e1c8aded4bc5c5a5596278d1f163d563e6586560d9b2afa95fa186dc231c4" +checksum = "609a4b66c3302e58ac44eb3c5c90bb5fdeb577ba1f1e9c1bce7d01b71ed5a636" dependencies = [ "quote 1.0.7", "syn 1.0.48", @@ -275,6 +275,19 @@ dependencies = [ "zeroize 1.1.1", ] +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits 0.2.14", + "time", + "winapi 0.3.9", +] + [[package]] name = "cipher" version = "0.2.1" @@ -691,7 +704,7 @@ checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -896,6 +909,7 @@ dependencies = [ "async-trait", "base64 0.12.3", "bech32", + "chrono", "clap", "clap_generate", "colored", @@ -926,7 +940,7 @@ dependencies = [ [[package]] name = "lnpbp" version = "0.2.0-beta.1" -source = "git+git://github.com/LNP-BP/rust-lnpbp#f3c1313a3232ee021f0f2fe889a53833c659bf79" +source = "git+git://github.com/LNP-BP/rust-lnpbp#d05e9b3bf01b23ecb66066a08deabf15116a3a48" dependencies = [ "amplify", "amplify_derive", @@ -935,6 +949,7 @@ dependencies = [ "bitcoin", "bitcoin_hashes 0.9.4", "chacha20poly1305", + "chrono", "deflate", "ed25519-dalek", "grin_secp256k1zkp", @@ -956,7 +971,7 @@ dependencies = [ [[package]] name = "lnpbp_derive" version = "0.2.0-beta.1" -source = "git+git://github.com/LNP-BP/rust-lnpbp#f3c1313a3232ee021f0f2fe889a53833c659bf79" +source = "git+git://github.com/LNP-BP/rust-lnpbp#d05e9b3bf01b23ecb66066a08deabf15116a3a48" dependencies = [ "amplify", "quote 1.0.7", @@ -966,7 +981,7 @@ dependencies = [ [[package]] name = "lnpbp_services" version = "0.2.0-beta.1" -source = "git+git://github.com/LNP-BP/rust-lnpbp#f3c1313a3232ee021f0f2fe889a53833c659bf79" +source = "git+git://github.com/LNP-BP/rust-lnpbp#d05e9b3bf01b23ecb66066a08deabf15116a3a48" dependencies = [ "amplify", "amplify_derive", @@ -1150,6 +1165,16 @@ dependencies = [ "syn 1.0.48", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg 1.0.1", + "num-traits 0.2.14", +] + [[package]] name = "num-traits" version = "0.1.43" @@ -1540,9 +1565,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" [[package]] name = "ring" @@ -2001,6 +2026,17 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + [[package]] name = "tinyvec" version = "0.3.4" @@ -2193,6 +2229,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasm-bindgen" version = "0.2.68" diff --git a/Cargo.toml b/Cargo.toml index 245003d..374cc3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,14 +34,16 @@ name = "lnp-cli" required-features = ["cli"] [dependencies] -# Rust language +# LNP/BP crates amplify = "~2.2.1" -amplify_derive = "~2.2.1" +amplify_derive = "~2.2.2" lnpbp = { git = "git://github.com/LNP-BP/rust-lnpbp", features = ["lnp", "url", "websockets"] } lnpbp_derive = { git = "git://github.com/LNP-BP/rust-lnpbp" } lnpbp_services = { git = "git://github.com/LNP-BP/rust-lnpbp" } lazy_static = "~1.4.0" +# Rust language nix = { version = "~0.19.0", optional = true } +chrono = "~0.4.19" # Bitcoin electrum-client = { version = "=0.3.0-beta.1", optional = true } # Serialization & parsing @@ -70,7 +72,7 @@ zmq = { version = "~0.9.2", optional = true } [build-dependencies] amplify = "~2.2.1" -amplify_derive = "~2.2.1" +amplify_derive = "~2.2.2" lnpbp = { git = "git://github.com/LNP-BP/rust-lnpbp", features = ["lnp", "url", "websockets"] } lnpbp_services = { git = "git://github.com/LNP-BP/rust-lnpbp" } clap = "=3.0.0-beta.2" @@ -117,8 +119,8 @@ shell = [ # This feature results in building with features not required for CLI node = ["serde", "lnpbp/keygen", "tokio", "lnpbp/tokio", "zmq", "lnpbp_services/node", "url", "lnpbp/url"] -serde = ["serde_crate", "serde_with", "toml", "amplify/serde", "lnpbp/serde", - "lnpbp_services/serde"] +serde = ["serde_crate", "serde_with", "serde_yaml", "toml", + "amplify/serde", "lnpbp/serde", "lnpbp_services/serde"] [package.metadata.configure_me] spec = "config_spec.toml" diff --git a/src/bin/lnp-cli.rs b/src/bin/lnp-cli.rs index 2b6fd93..5269d61 100644 --- a/src/bin/lnp-cli.rs +++ b/src/bin/lnp-cli.rs @@ -20,7 +20,7 @@ extern crate log; use clap::Clap; use lnp_node::cli::{Opts, Runtime}; -use lnp_node::Config; +use lnp_node::{Config, LogStyle}; use lnpbp_services::shell::Exec; fn main() { @@ -42,5 +42,5 @@ fn main() { trace!("Executing command: {:?}", opts.command); opts.command .exec(&mut runtime) - .unwrap_or_else(|err| error!("{}", err)); + .unwrap_or_else(|err| eprintln!("{}", err.err())); } diff --git a/src/channeld/runtime.rs b/src/channeld/runtime.rs index cc0a4f3..e772872 100644 --- a/src/channeld/runtime.rs +++ b/src/channeld/runtime.rs @@ -73,7 +73,7 @@ impl Runtime { ) -> Result<(), Error> { let mut notify_cli = None; match request { - Request::LnpwpMessage(Messages::AcceptChannel(accept_channel)) => { + Request::SendMessage(Messages::AcceptChannel(accept_channel)) => { info!( "{} from the remote peer {} with temporary id {}", "Accepting channel".promo(), @@ -86,7 +86,7 @@ impl Runtime { ))); } - Request::LnpwpMessage(_) => { + Request::SendMessage(_) => { // Ignore the rest of LN peer messages } @@ -131,7 +131,7 @@ impl Runtime { ServiceBus::Msg, self.identity(), peerd, - Request::LnpwpMessage(Messages::OpenChannel(channel_req)), + Request::SendMessage(Messages::OpenChannel(channel_req)), )?; notify_cli = Some((report_to, msg)) } @@ -168,7 +168,7 @@ impl Runtime { ServiceBus::Msg, self.identity(), peerd, - Request::LnpwpMessage(Messages::AcceptChannel( + Request::SendMessage(Messages::AcceptChannel( accept_channel, )), )?; diff --git a/src/cli/command.rs b/src/cli/command.rs index 62387a5..fddbc1c 100644 --- a/src/cli/command.rs +++ b/src/cli/command.rs @@ -30,6 +30,21 @@ impl Exec for Command { fn exec(&self, runtime: &mut Self::Runtime) -> Result<(), Self::Error> { debug!("Performing {:?}: {}", self, self); match self { + Command::Info => { + runtime.request(ServiceId::Lnpd, Request::GetInfo)?; + runtime.report_response()?; + } + + Command::Peers => { + runtime.request(ServiceId::Lnpd, Request::ListPeers)?; + runtime.report_response()?; + } + + Command::Channels => { + runtime.request(ServiceId::Lnpd, Request::ListChannels)?; + runtime.report_response()?; + } + Command::Listen { ip_addr, port, diff --git a/src/cli/runtime.rs b/src/cli/runtime.rs index 9fa24ee..5bae201 100644 --- a/src/cli/runtime.rs +++ b/src/cli/runtime.rs @@ -69,6 +69,23 @@ impl Runtime { Ok(()) } + pub fn report_response(&mut self) -> Result<(), Error> { + for (_, _, rep) in self.esb.recv_poll()? { + match rep { + Request::Failure(fail) => { + eprintln!( + "{}: {}", + "Request failure".err(), + fail.err_details() + ); + Err(Error::from(fail))? + } + resp => println!("{:#}", resp), + } + } + Ok(()) + } + pub fn report_progress(&mut self) -> Result { let mut counter = 0; let mut finished = false; diff --git a/src/gossipd/runtime.rs b/src/gossipd/runtime.rs index 65cb951..b26946a 100644 --- a/src/gossipd/runtime.rs +++ b/src/gossipd/runtime.rs @@ -71,7 +71,7 @@ impl Runtime { request: Request, ) -> Result<(), Error> { match request { - Request::LnpwpMessage(_message) => { + Request::SendMessage(_message) => { // TODO: Process message } _ => { diff --git a/src/lib.rs b/src/lib.rs index a788a03..7ec0d04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,10 @@ extern crate amplify_derive; #[macro_use] extern crate lnpbp_derive; +#[cfg(feature = "serde")] +#[macro_use] +extern crate serde_crate as serde; + #[cfg(feature = "shell")] extern crate clap; #[cfg(feature = "shell")] diff --git a/src/lnpd/runtime.rs b/src/lnpd/runtime.rs index 2212772..14ff863 100644 --- a/src/lnpd/runtime.rs +++ b/src/lnpd/runtime.rs @@ -19,6 +19,7 @@ use std::ffi::OsStr; use std::io; use std::net::SocketAddr; use std::process; +use std::time::{Duration, SystemTime}; use lnpbp::bitcoin::hashes::hex::ToHex; use lnpbp::bitcoin::secp256k1; @@ -26,8 +27,9 @@ use lnpbp::lnp::{ message, ChannelId, Messages, NodeAddr, RemoteSocketAddr, TypedEnum, }; use lnpbp_services::esb::{self, Handler}; +use lnpbp_services::rpc::Failure; -use crate::rpc::request::{IntoProgressOrFalure, OptionDetails}; +use crate::rpc::request::{IntoProgressOrFalure, NodeInfo, OptionDetails}; use crate::rpc::{request, Request, ServiceBus}; use crate::{Config, Error, LogStyle, Service, ServiceId}; @@ -35,6 +37,8 @@ pub fn run(config: Config, node_id: secp256k1::PublicKey) -> Result<(), Error> { let runtime = Runtime { identity: ServiceId::Lnpd, node_id, + listens: none!(), + started: SystemTime::now(), connections: none!(), channels: none!(), spawning_services: none!(), @@ -48,6 +52,8 @@ pub fn run(config: Config, node_id: secp256k1::PublicKey) -> Result<(), Error> { pub struct Runtime { identity: ServiceId, node_id: secp256k1::PublicKey, + listens: HashSet, + started: SystemTime, connections: HashSet, channels: HashSet, spawning_services: HashMap, @@ -100,12 +106,12 @@ impl Runtime { // Ignoring; this is used to set remote identity at ZMQ level } - Request::LnpwpMessage(Messages::OpenChannel(open_channel)) => { + Request::SendMessage(Messages::OpenChannel(open_channel)) => { info!("Creating channel by peer request from {}", source); self.create_channel(source, None, open_channel, true)?; } - Request::LnpwpMessage(_) => { + Request::SendMessage(_) => { // Ignore the rest of LN peer messages } @@ -235,32 +241,85 @@ impl Runtime { } } + Request::GetInfo => { + senders.send_to( + ServiceBus::Ctl, + ServiceId::Lnpd, + source, + Request::NodeInfo(NodeInfo { + node_id: self.node_id, + listens: self.listens.iter().cloned().collect(), + uptime: SystemTime::now() + .duration_since(self.started) + .unwrap_or(Duration::from_secs(0)), + since: self + .started + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or(Duration::from_secs(0)) + .as_secs(), + peers: self.connections.len(), + channels: self.channels.len(), + }), + )?; + } + + Request::ListPeers => { + senders.send_to( + ServiceBus::Ctl, + ServiceId::Lnpd, + source, + Request::PeerList(vec![].into()), + )?; + } + + Request::ListChannels => { + senders.send_to( + ServiceBus::Ctl, + ServiceId::Lnpd, + source, + Request::ChannelList(vec![].into()), + )?; + } + Request::Listen(addr) => { let addr_str = addr.addr(); - info!( - "{} for incoming LN peer connections on {}", - "Starting listener".promo(), - addr_str - ); - let resp = self.listen(addr.clone()); - match resp { + if self.listens.contains(&addr) { + let msg = format!( + "Listener on {} already exists, ignoring request", + addr + ); + warn!("{}", msg.err()); + notify_cli = Some(( + Some(source.clone()), + Request::Failure(Failure { code: 1, info: msg }), + )); + } else { + self.listens.insert(addr); + info!( + "{} for incoming LN peer connections on {}", + "Starting listener".promo(), + addr_str + ); + let resp = self.listen(addr); + match resp { Ok(_) => info!("Connection daemon {} for incoming LN peer connections on {}", "listens".ended(), addr_str), Err(ref err) => error!("{}", err.err()) } - senders.send_to( - ServiceBus::Ctl, - ServiceId::Lnpd, - source.clone(), - resp.into_progress_or_failure(), - )?; - notify_cli = Some(( - Some(source.clone()), - Request::Success(OptionDetails::with(format!( - "Node {} listens for connections on {}", - self.node_id, addr - ))), - )); + senders.send_to( + ServiceBus::Ctl, + ServiceId::Lnpd, + source.clone(), + resp.into_progress_or_failure(), + )?; + notify_cli = Some(( + Some(source.clone()), + Request::Success(OptionDetails::with(format!( + "Node {} listens for connections on {}", + self.node_id, addr + ))), + )); + } } Request::ConnectPeer(addr) => { diff --git a/src/peerd/runtime.rs b/src/peerd/runtime.rs index 9c1e7fd..fd27ab0 100644 --- a/src/peerd/runtime.rs +++ b/src/peerd/runtime.rs @@ -128,7 +128,7 @@ impl peer::Handler for ListenerRuntime { fn handle(&mut self, message: Messages) -> Result<(), Self::Error> { // Forwarding all received messages to the runtime trace!("LNPWP message details: {:?}", message); - self.send_over_bridge(Request::LnpwpMessage(message)) + self.send_over_bridge(Request::SendMessage(message)) } fn handle_err(&mut self, err: Self::Error) -> Result<(), Self::Error> { @@ -219,7 +219,7 @@ impl Runtime { request: Request, ) -> Result<(), Error> { match request { - Request::LnpwpMessage(message) => { + Request::SendMessage(message) => { // 1. Check permissions // 2. Forward to the remote peer debug!("Forwarding LN peer message to the remote peer"); @@ -267,14 +267,14 @@ impl Runtime { self.ping()?; } - Request::LnpwpMessage(Messages::Ping(message::Ping { + Request::SendMessage(Messages::Ping(message::Ping { pong_size, .. })) => { self.pong(pong_size)?; } - Request::LnpwpMessage(Messages::Pong(noise)) => { + Request::SendMessage(Messages::Pong(noise)) => { match self.awaited_pong { None => error!("Unexpected pong from the remote peer"), Some(len) if len as usize != noise.len() => warn!( @@ -285,7 +285,7 @@ impl Runtime { self.awaited_pong = None; } - Request::LnpwpMessage(Messages::OpenChannel(_)) => { + Request::SendMessage(Messages::OpenChannel(_)) => { senders.send_to( ServiceBus::Msg, self.identity(), @@ -294,18 +294,18 @@ impl Runtime { )?; } - Request::LnpwpMessage(Messages::AcceptChannel(accept_channel)) => { + Request::SendMessage(Messages::AcceptChannel(accept_channel)) => { senders.send_to( ServiceBus::Msg, self.identity(), accept_channel.temporary_channel_id.into(), - Request::LnpwpMessage(Messages::AcceptChannel( + Request::SendMessage(Messages::AcceptChannel( accept_channel, )), )?; } - Request::LnpwpMessage(message) => { + Request::SendMessage(message) => { // 1. Check permissions // 2. Forward to the corresponding daemon debug!("Got peer LNPWP message {}", message); diff --git a/src/routed/runtime.rs b/src/routed/runtime.rs index f22649e..23d926d 100644 --- a/src/routed/runtime.rs +++ b/src/routed/runtime.rs @@ -71,7 +71,7 @@ impl Runtime { request: Request, ) -> Result<(), Error> { match request { - Request::LnpwpMessage(_message) => { + Request::SendMessage(_message) => { // TODO: Process message } _ => { diff --git a/src/rpc/request.rs b/src/rpc/request.rs index ba621f5..eebc9db 100644 --- a/src/rpc/request.rs +++ b/src/rpc/request.rs @@ -13,11 +13,16 @@ // If not, see . use amplify::Wrapper; -use std::fmt::{self, Display, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; +use std::time::Duration; +use lnpbp::bitcoin::{secp256k1, Txid}; +use lnpbp::bp::chain::AssetId; use lnpbp::lnp::{ - message, rpc_connection, Invoice, Messages, NodeAddr, RemoteSocketAddr, + message, rpc_connection, ChannelId, ChannelState, Invoice, Messages, + NodeAddr, RemoteSocketAddr, }; +use lnpbp::strict_encoding::{self, StrictDecode, StrictEncode}; use lnpbp_services::rpc::Failure; use crate::ServiceId; @@ -31,56 +36,221 @@ pub enum Request { Hello, #[lnp_api(type = 1)] - #[display("lnpwp({0})")] - LnpwpMessage(Messages), + #[display("send_message({0})")] + SendMessage(Messages), // Can be issued from `cli` to `lnpd` - #[lnp_api(type = 2)] + #[lnp_api(type = 100)] + #[display("node_info()")] + GetInfo, + + // Can be issued from `cli` to `lnpd` + #[lnp_api(type = 101)] + #[display("list_peers()")] + ListPeers, + + // Can be issued from `cli` to `lnpd` + #[lnp_api(type = 102)] + #[display("list_channels()")] + ListChannels, + + // Can be issued from `cli` to `lnpd` + #[lnp_api(type = 200)] #[display("listen({0})")] Listen(RemoteSocketAddr), // Can be issued from `cli` to `lnpd` - #[lnp_api(type = 3)] + #[lnp_api(type = 201)] #[display("connect({0})")] ConnectPeer(NodeAddr), // Can be issued from `cli` to a specific `peerd` - #[lnp_api(type = 4)] + #[lnp_api(type = 202)] #[display("ping_peer()")] PingPeer, // Can be issued from `cli` to `lnpd` - #[lnp_api(type = 5)] + #[lnp_api(type = 203)] #[display("create_channel_with(...)")] OpenChannelWith(CreateChannel), - #[lnp_api(type = 6)] + #[lnp_api(type = 204)] #[display("accept_channel_from(...)")] AcceptChannelFrom(CreateChannel), // Can be issued from `cli` to a specific `peerd` - #[lnp_api(type = 7)] + #[lnp_api(type = 205)] #[display("pay_invoice({0})")] PayInvoice(Invoice), // Responses to CLI // ---------------- - #[lnp_api(type = 102)] + #[lnp_api(type = 1002)] #[display("progress({0})")] Progress(String), - #[lnp_api(type = 101)] + #[lnp_api(type = 1001)] #[display("success({0})")] Success(OptionDetails), - #[lnp_api(type = 100)] + #[lnp_api(type = 1000)] #[display("failure({0:#})")] #[from] Failure(Failure), + + #[lnp_api(type = 1100)] + #[display("nonde_info({0})", alt = "{0:#}")] + #[from] + NodeInfo(NodeInfo), + + #[lnp_api(type = 1101)] + #[display("peer_list({0})", alt = "{0:#}")] + #[from] + PeerList(List), + + #[lnp_api(type = 1102)] + #[display("channel_list({0})", alt = "{0:#}")] + #[from] + ChannelList(List), } impl rpc_connection::Request for Request {} +// TODO: Move to amplify +#[cfg(feature = "serde")] +pub trait ToYamlString +where + Self: serde::Serialize, +{ + fn to_yaml_string(&self) -> String { + serde_yaml::to_string(self).expect("internal YAML serialization error") + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Display, StrictEncode, StrictDecode)] +#[display("{peerd}, ...")] +pub struct CreateChannel { + pub channel_req: message::OpenChannel, + pub peerd: ServiceId, + pub report_to: Option, +} + +#[derive(Clone, PartialEq, Eq, Debug, Display, StrictEncode, StrictDecode)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] +#[display(NodeInfo::to_yaml_string)] +pub struct NodeInfo { + pub node_id: secp256k1::PublicKey, + pub listens: Vec, + pub uptime: Duration, + pub since: u64, + pub peers: usize, + pub channels: usize, +} + +#[derive(Clone, PartialEq, Eq, Debug, Display, StrictEncode, StrictDecode)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] +#[display(PeerInfo::to_yaml_string)] +pub struct PeerInfo { + pub node_id: secp256k1::PublicKey, + pub uptime: Duration, + pub since: i64, + pub channels: usize, +} + +#[derive(Clone, PartialEq, Eq, Debug, Display, StrictEncode, StrictDecode)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] +#[display(ChannelInfo::to_yaml_string)] +pub struct ChannelInfo { + pub channel_id: ChannelId, + pub state: ChannelState, + pub capacities: (u64, u64), + pub assets: Vec, + // assets: HashMap, + pub funding_tx: Txid, + pub remote_peers: Vec, + pub uptime: Duration, + pub since: i64, + pub total_updates: u64, + pub pending_updates: u16, + pub max_updates: u16, +} + +#[cfg(feature = "serde")] +impl ToYamlString for NodeInfo {} +#[cfg(feature = "serde")] +impl ToYamlString for PeerInfo {} +#[cfg(feature = "serde")] +impl ToYamlString for ChannelInfo {} + +#[derive( + Wrapper, Clone, PartialEq, Eq, Debug, From, StrictEncode, StrictDecode, +)] +#[wrapper(IndexRange)] +pub struct List(Vec) +where + T: Clone + + PartialEq + + Eq + + Debug + + Display + + StrictEncode + + StrictDecode; + +#[cfg(feature = "serde")] +impl<'a, T> Display for List +where + T: Clone + + PartialEq + + Eq + + Debug + + Display + + serde::Serialize + + StrictEncode + + StrictDecode, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str( + &serde_yaml::to_string(self) + .expect("internal YAML serialization error"), + ) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for List +where + T: Clone + + PartialEq + + Eq + + Debug + + Display + + serde::Serialize + + StrictEncode + + StrictDecode, +{ + fn serialize( + &self, + serializer: S, + ) -> Result<::Ok, ::Error> + where + S: serde::Serializer, + { + self.as_inner().serialize(serializer) + } +} + #[derive( Wrapper, Clone, @@ -113,14 +283,6 @@ impl OptionDetails { } } -#[derive(Clone, PartialEq, Eq, Debug, Display, StrictEncode, StrictDecode)] -#[display("{peerd}, ...")] -pub struct CreateChannel { - pub channel_req: message::OpenChannel, - pub peerd: ServiceId, - pub report_to: Option, -} - impl From for Request { fn from(err: crate::Error) -> Self { Request::Failure(Failure::from(err))