diff --git a/lightning-c-bindings/include/lightning.h b/lightning-c-bindings/include/lightning.h index 01a252ee396..28e54f4f3a9 100644 --- a/lightning-c-bindings/include/lightning.h +++ b/lightning-c-bindings/include/lightning.h @@ -4150,6 +4150,8 @@ struct LDKCResult_ChannelInfoDecodeErrorZ CResult_ChannelInfoDecodeErrorZ_err(st void CResult_ChannelInfoDecodeErrorZ_free(struct LDKCResult_ChannelInfoDecodeErrorZ _res); +struct LDKCResult_ChannelInfoDecodeErrorZ CResult_ChannelInfoDecodeErrorZ_clone(const struct LDKCResult_ChannelInfoDecodeErrorZ *NONNULL_PTR orig); + struct LDKCResult_RoutingFeesDecodeErrorZ CResult_RoutingFeesDecodeErrorZ_ok(struct LDKRoutingFees o); struct LDKCResult_RoutingFeesDecodeErrorZ CResult_RoutingFeesDecodeErrorZ_err(struct LDKDecodeError e); @@ -4184,6 +4186,8 @@ struct LDKCResult_NetworkGraphDecodeErrorZ CResult_NetworkGraphDecodeErrorZ_err( void CResult_NetworkGraphDecodeErrorZ_free(struct LDKCResult_NetworkGraphDecodeErrorZ _res); +struct LDKCResult_NetworkGraphDecodeErrorZ CResult_NetworkGraphDecodeErrorZ_clone(const struct LDKCResult_NetworkGraphDecodeErrorZ *NONNULL_PTR orig); + struct LDKC2Tuple_usizeTransactionZ C2Tuple_usizeTransactionZ_new(uintptr_t a, struct LDKTransaction b); void C2Tuple_usizeTransactionZ_free(struct LDKC2Tuple_usizeTransactionZ _res); @@ -6149,6 +6153,18 @@ struct LDKDecodeError DecodeError_clone(const struct LDKDecodeError *NONNULL_PTR void Init_free(struct LDKInit this_ptr); +/** + * The relevant features which the sender supports + */ +struct LDKInitFeatures Init_get_features(const struct LDKInit *NONNULL_PTR this_ptr); + +/** + * The relevant features which the sender supports + */ +void Init_set_features(struct LDKInit *NONNULL_PTR this_ptr, struct LDKInitFeatures val); + +MUST_USE_RES struct LDKInit Init_new(struct LDKInitFeatures features_arg); + struct LDKInit Init_clone(const struct LDKInit *NONNULL_PTR orig); void ErrorMessage_free(struct LDKErrorMessage this_ptr); @@ -8772,6 +8788,8 @@ struct LDKCResult_RouteLightningErrorZ get_route(struct LDKPublicKey our_node_id void NetworkGraph_free(struct LDKNetworkGraph this_ptr); +struct LDKNetworkGraph NetworkGraph_clone(const struct LDKNetworkGraph *NONNULL_PTR orig); + void LockedNetworkGraph_free(struct LDKLockedNetworkGraph this_ptr); void NetGraphMsgHandler_free(struct LDKNetGraphMsgHandler this_ptr); @@ -8952,6 +8970,8 @@ struct LDKChannelAnnouncement ChannelInfo_get_announcement_message(const struct */ void ChannelInfo_set_announcement_message(struct LDKChannelInfo *NONNULL_PTR this_ptr, struct LDKChannelAnnouncement val); +struct LDKChannelInfo ChannelInfo_clone(const struct LDKChannelInfo *NONNULL_PTR orig); + struct LDKCVec_u8Z ChannelInfo_write(const struct LDKChannelInfo *NONNULL_PTR obj); struct LDKCResult_ChannelInfoDecodeErrorZ ChannelInfo_read(struct LDKu8slice ser); diff --git a/lightning-c-bindings/src/c_types/derived.rs b/lightning-c-bindings/src/c_types/derived.rs index d58c56b45ca..f2211f191ee 100644 --- a/lightning-c-bindings/src/c_types/derived.rs +++ b/lightning-c-bindings/src/c_types/derived.rs @@ -1940,6 +1940,21 @@ impl From Self { + if self.result_ok { + Self { result_ok: true, contents: CResult_ChannelInfoDecodeErrorZPtr { + result: Box::into_raw(Box::new(::clone(unsafe { &*self.contents.result }))) + } } + } else { + Self { result_ok: false, contents: CResult_ChannelInfoDecodeErrorZPtr { + err: Box::into_raw(Box::new(::clone(unsafe { &*self.contents.err }))) + } } + } + } +} +#[no_mangle] +pub extern "C" fn CResult_ChannelInfoDecodeErrorZ_clone(orig: &CResult_ChannelInfoDecodeErrorZ) -> CResult_ChannelInfoDecodeErrorZ { orig.clone() } #[repr(C)] pub union CResult_RoutingFeesDecodeErrorZPtr { pub result: *mut crate::routing::network_graph::RoutingFees, @@ -2305,6 +2320,21 @@ impl From Self { + if self.result_ok { + Self { result_ok: true, contents: CResult_NetworkGraphDecodeErrorZPtr { + result: Box::into_raw(Box::new(::clone(unsafe { &*self.contents.result }))) + } } + } else { + Self { result_ok: false, contents: CResult_NetworkGraphDecodeErrorZPtr { + err: Box::into_raw(Box::new(::clone(unsafe { &*self.contents.err }))) + } } + } + } +} +#[no_mangle] +pub extern "C" fn CResult_NetworkGraphDecodeErrorZ_clone(orig: &CResult_NetworkGraphDecodeErrorZ) -> CResult_NetworkGraphDecodeErrorZ { orig.clone() } #[repr(C)] pub struct C2Tuple_usizeTransactionZ { pub a: usize, diff --git a/lightning-c-bindings/src/ln/msgs.rs b/lightning-c-bindings/src/ln/msgs.rs index 308730f5350..dd60efa03c1 100644 --- a/lightning-c-bindings/src/ln/msgs.rs +++ b/lightning-c-bindings/src/ln/msgs.rs @@ -113,6 +113,24 @@ impl Init { ret } } +/// The relevant features which the sender supports +#[no_mangle] +pub extern "C" fn Init_get_features(this_ptr: &Init) -> crate::ln::features::InitFeatures { + let mut inner_val = &mut unsafe { &mut *this_ptr.inner }.features; + crate::ln::features::InitFeatures { inner: unsafe { ( (&((*inner_val)) as *const _) as *mut _) }, is_owned: false } +} +/// The relevant features which the sender supports +#[no_mangle] +pub extern "C" fn Init_set_features(this_ptr: &mut Init, mut val: crate::ln::features::InitFeatures) { + unsafe { &mut *this_ptr.inner }.features = *unsafe { Box::from_raw(val.take_inner()) }; +} +#[must_use] +#[no_mangle] +pub extern "C" fn Init_new(mut features_arg: crate::ln::features::InitFeatures) -> Init { + Init { inner: Box::into_raw(Box::new(nativeInit { + features: *unsafe { Box::from_raw(features_arg.take_inner()) }, + })), is_owned: true } +} impl Clone for Init { fn clone(&self) -> Self { Self { diff --git a/lightning-c-bindings/src/routing/network_graph.rs b/lightning-c-bindings/src/routing/network_graph.rs index ad4c0c3b331..157dbc4225e 100644 --- a/lightning-c-bindings/src/routing/network_graph.rs +++ b/lightning-c-bindings/src/routing/network_graph.rs @@ -42,6 +42,24 @@ impl NetworkGraph { ret } } +impl Clone for NetworkGraph { + fn clone(&self) -> Self { + Self { + inner: if self.inner.is_null() { std::ptr::null_mut() } else { + Box::into_raw(Box::new(unsafe { &*self.inner }.clone())) }, + is_owned: true, + } + } +} +#[allow(unused)] +/// Used only if an object of this type is returned as a trait impl by a method +pub(crate) extern "C" fn NetworkGraph_clone_void(this_ptr: *const c_void) -> *mut c_void { + Box::into_raw(Box::new(unsafe { (*(this_ptr as *mut nativeNetworkGraph)).clone() })) as *mut c_void +} +#[no_mangle] +pub extern "C" fn NetworkGraph_clone(orig: &NetworkGraph) -> NetworkGraph { + orig.clone() +} use lightning::routing::network_graph::LockedNetworkGraph as nativeLockedNetworkGraphImport; type nativeLockedNetworkGraph = nativeLockedNetworkGraphImport<'static>; @@ -559,6 +577,24 @@ pub extern "C" fn ChannelInfo_set_announcement_message(this_ptr: &mut ChannelInf let mut local_val = if val.inner.is_null() { None } else { Some( { *unsafe { Box::from_raw(val.take_inner()) } }) }; unsafe { &mut *this_ptr.inner }.announcement_message = local_val; } +impl Clone for ChannelInfo { + fn clone(&self) -> Self { + Self { + inner: if self.inner.is_null() { std::ptr::null_mut() } else { + Box::into_raw(Box::new(unsafe { &*self.inner }.clone())) }, + is_owned: true, + } + } +} +#[allow(unused)] +/// Used only if an object of this type is returned as a trait impl by a method +pub(crate) extern "C" fn ChannelInfo_clone_void(this_ptr: *const c_void) -> *mut c_void { + Box::into_raw(Box::new(unsafe { (*(this_ptr as *mut nativeChannelInfo)).clone() })) as *mut c_void +} +#[no_mangle] +pub extern "C" fn ChannelInfo_clone(orig: &ChannelInfo) -> ChannelInfo { + orig.clone() +} #[no_mangle] pub extern "C" fn ChannelInfo_write(obj: &ChannelInfo) -> crate::c_types::derived::CVec_u8Z { crate::c_types::serialize_obj(unsafe { &*unsafe { &*obj }.inner }) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 2c3e7df717b..5319742cbe2 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -66,16 +66,14 @@ pub enum DecodeError { } /// An init message to be sent or received from a peer -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct Init { - #[cfg(not(feature = "fuzztarget"))] - pub(crate) features: InitFeatures, - #[cfg(feature = "fuzztarget")] + /// The relevant features which the sender supports pub features: InitFeatures, } /// An error message to be sent or received from a peer -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct ErrorMessage { /// The channel ID involved in the error pub channel_id: [u8; 32], @@ -87,7 +85,7 @@ pub struct ErrorMessage { } /// A ping message to be sent or received from a peer -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct Ping { /// The desired response length pub ponglen: u16, @@ -97,7 +95,7 @@ pub struct Ping { } /// A pong message to be sent or received from a peer -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct Pong { /// The pong packet size. /// This field is not sent on the wire. byteslen zeros are sent. @@ -105,7 +103,7 @@ pub struct Pong { } /// An open_channel message to be sent or received from a peer -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct OpenChannel { /// The genesis hash of the blockchain where the channel is to be opened pub chain_hash: BlockHash, @@ -148,7 +146,7 @@ pub struct OpenChannel { } /// An accept_channel message to be sent or received from a peer -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct AcceptChannel { /// A temporary channel ID, until the funding outpoint is announced pub temporary_channel_id: [u8; 32], @@ -183,7 +181,7 @@ pub struct AcceptChannel { } /// A funding_created message to be sent or received from a peer -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct FundingCreated { /// A temporary channel ID, until the funding is established pub temporary_channel_id: [u8; 32], @@ -196,7 +194,7 @@ pub struct FundingCreated { } /// A funding_signed message to be sent or received from a peer -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct FundingSigned { /// The channel ID pub channel_id: [u8; 32], @@ -205,7 +203,7 @@ pub struct FundingSigned { } /// A funding_locked message to be sent or received from a peer -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct FundingLocked { /// The channel ID pub channel_id: [u8; 32], @@ -214,7 +212,7 @@ pub struct FundingLocked { } /// A shutdown message to be sent or received from a peer -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct Shutdown { /// The channel ID pub channel_id: [u8; 32], @@ -224,7 +222,7 @@ pub struct Shutdown { } /// A closing_signed message to be sent or received from a peer -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct ClosingSigned { /// The channel ID pub channel_id: [u8; 32], @@ -235,7 +233,7 @@ pub struct ClosingSigned { } /// An update_add_htlc message to be sent or received from a peer -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct UpdateAddHTLC { /// The channel ID pub channel_id: [u8; 32], @@ -251,7 +249,7 @@ pub struct UpdateAddHTLC { } /// An update_fulfill_htlc message to be sent or received from a peer -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct UpdateFulfillHTLC { /// The channel ID pub channel_id: [u8; 32], @@ -262,7 +260,7 @@ pub struct UpdateFulfillHTLC { } /// An update_fail_htlc message to be sent or received from a peer -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct UpdateFailHTLC { /// The channel ID pub channel_id: [u8; 32], @@ -272,7 +270,7 @@ pub struct UpdateFailHTLC { } /// An update_fail_malformed_htlc message to be sent or received from a peer -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct UpdateFailMalformedHTLC { /// The channel ID pub channel_id: [u8; 32], @@ -284,7 +282,7 @@ pub struct UpdateFailMalformedHTLC { } /// A commitment_signed message to be sent or received from a peer -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct CommitmentSigned { /// The channel ID pub channel_id: [u8; 32], @@ -295,7 +293,7 @@ pub struct CommitmentSigned { } /// A revoke_and_ack message to be sent or received from a peer -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct RevokeAndACK { /// The channel ID pub channel_id: [u8; 32], @@ -306,7 +304,7 @@ pub struct RevokeAndACK { } /// An update_fee message to be sent or received from a peer -#[derive(PartialEq, Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct UpdateFee { /// The channel ID pub channel_id: [u8; 32], @@ -314,7 +312,7 @@ pub struct UpdateFee { pub feerate_per_kw: u32, } -#[derive(PartialEq, Clone)] +#[derive(Clone, Debug, PartialEq)] /// Proof that the sender knows the per-commitment secret of the previous commitment transaction. /// This is used to convince the recipient that the channel is at a certain commitment /// number even if they lost that data due to a local failure. Of course, the peer may lie @@ -328,7 +326,7 @@ pub struct DataLossProtect { } /// A channel_reestablish message to be sent or received from a peer -#[derive(PartialEq, Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct ChannelReestablish { /// The channel ID pub channel_id: [u8; 32], @@ -341,7 +339,7 @@ pub struct ChannelReestablish { } /// An announcement_signatures message to be sent or received from a peer -#[derive(PartialEq, Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct AnnouncementSignatures { /// The channel ID pub channel_id: [u8; 32], @@ -354,7 +352,7 @@ pub struct AnnouncementSignatures { } /// An address which can be used to connect to a remote peer -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum NetAddress { /// An IPv4 address/port on which the peer is listening. IPv4 { @@ -472,7 +470,7 @@ impl Readable for Result { } /// The unsigned part of a node_announcement -#[derive(PartialEq, Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UnsignedNodeAnnouncement { /// The advertised features pub features: NodeFeatures, @@ -491,7 +489,7 @@ pub struct UnsignedNodeAnnouncement { pub(crate) excess_address_data: Vec, pub(crate) excess_data: Vec, } -#[derive(PartialEq, Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] /// A node_announcement message to be sent or received from a peer pub struct NodeAnnouncement { /// The signature by the node key @@ -501,7 +499,7 @@ pub struct NodeAnnouncement { } /// The unsigned part of a channel_announcement -#[derive(PartialEq, Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UnsignedChannelAnnouncement { /// The advertised channel features pub features: ChannelFeatures, @@ -520,7 +518,7 @@ pub struct UnsignedChannelAnnouncement { pub(crate) excess_data: Vec, } /// A channel_announcement message to be sent or received from a peer -#[derive(PartialEq, Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ChannelAnnouncement { /// Authentication of the announcement by the first public node pub node_signature_1: Signature, @@ -535,7 +533,7 @@ pub struct ChannelAnnouncement { } /// The unsigned part of a channel_update -#[derive(PartialEq, Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UnsignedChannelUpdate { /// The genesis hash of the blockchain where the channel is to be opened pub chain_hash: BlockHash, @@ -558,7 +556,7 @@ pub struct UnsignedChannelUpdate { pub(crate) excess_data: Vec, } /// A channel_update message to be sent or received from a peer -#[derive(PartialEq, Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ChannelUpdate { /// A signature of the channel update pub signature: Signature, @@ -570,7 +568,7 @@ pub struct ChannelUpdate { /// UTXOs in a range of blocks. The recipient of a query makes a best /// effort to reply to the query using one or more reply_channel_range /// messages. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct QueryChannelRange { /// The genesis hash of the blockchain being queried pub chain_hash: BlockHash, @@ -587,7 +585,7 @@ pub struct QueryChannelRange { /// not be a perfect view of the network. The short_channel_ids in the /// reply are encoded. We only support encoding_type=0 uncompressed /// serialization and do not support encoding_type=1 zlib serialization. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ReplyChannelRange { /// The genesis hash of the blockchain being queried pub chain_hash: BlockHash, @@ -609,7 +607,7 @@ pub struct ReplyChannelRange { /// reply_short_channel_ids_end message. The short_channel_ids sent in /// this query are encoded. We only support encoding_type=0 uncompressed /// serialization and do not support encoding_type=1 zlib serialization. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct QueryShortChannelIds { /// The genesis hash of the blockchain being queried pub chain_hash: BlockHash, @@ -621,7 +619,7 @@ pub struct QueryShortChannelIds { /// query_short_channel_ids message. The query recipient makes a best /// effort to respond based on their local network view which may not be /// a perfect view of the network. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ReplyShortChannelIdsEnd { /// The genesis hash of the blockchain that was queried pub chain_hash: BlockHash, @@ -633,7 +631,7 @@ pub struct ReplyShortChannelIdsEnd { /// A gossip_timestamp_filter message is used by a node to request /// gossip relay for messages in the requested time range when the /// gossip_queries feature has been negotiated. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct GossipTimestampFilter { /// The genesis hash of the blockchain for channel and node information pub chain_hash: BlockHash, @@ -650,7 +648,7 @@ enum EncodingType { } /// Used to put an error message in a LightningError -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum ErrorAction { /// The peer took some action which made us think they were useless. Disconnect them. DisconnectPeer { @@ -667,7 +665,7 @@ pub enum ErrorAction { } /// An Err type for failure to process messages. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct LightningError { /// A human-readable message describing the error pub err: String, @@ -677,7 +675,7 @@ pub struct LightningError { /// Struct used to return values from revoke_and_ack messages, containing a bunch of commitment /// transaction updates if they were pending. -#[derive(PartialEq, Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct CommitmentUpdate { /// update_add_htlc messages which should be sent pub update_add_htlcs: Vec, @@ -696,7 +694,7 @@ pub struct CommitmentUpdate { /// The information we received from a peer along the route of a payment we originated. This is /// returned by ChannelMessageHandler::handle_update_fail_htlc to be passed into /// RoutingMessageHandler::handle_htlc_fail_channel_update to update our network map. -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub enum HTLCFailChannelUpdate { /// We received an error which included a full ChannelUpdate message. ChannelUpdateMessage { @@ -727,7 +725,7 @@ pub enum HTLCFailChannelUpdate { /// OptionalFeild simply gets Present if there are enough bytes to read into it), we have a /// separate enum type for them. /// (C-not exported) due to a free generic in T -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum OptionalField { /// Optional field is included in message Present(T), @@ -912,7 +910,13 @@ impl PartialEq for OnionPacket { } } -#[derive(Clone, PartialEq)] +impl fmt::Debug for OnionPacket { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!("OnionPacket version {} with hmac {:?}", self.version, &self.hmac[..])) + } +} + +#[derive(Clone, Debug, PartialEq)] pub(crate) struct OnionErrorPacket { // This really should be a constant size slice, but the spec lets these things be up to 128KB? // (TODO) We limit it in decode to much lower... @@ -932,12 +936,6 @@ impl fmt::Display for DecodeError { } } -impl fmt::Debug for LightningError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(self.err.as_str()) - } -} - impl From<::std::io::Error> for DecodeError { fn from(e: ::std::io::Error) -> Self { if e.kind() == ::std::io::ErrorKind::UnexpectedEof { diff --git a/lightning/src/routing/network_graph.rs b/lightning/src/routing/network_graph.rs index 4c5e718fed1..d9a1d8279de 100644 --- a/lightning/src/routing/network_graph.rs +++ b/lightning/src/routing/network_graph.rs @@ -45,7 +45,7 @@ use bitcoin::hashes::hex::ToHex; const MAX_EXCESS_BYTES_FOR_RELAY: usize = 1024; /// Represents the network as nodes and channels between them -#[derive(PartialEq)] +#[derive(Clone, PartialEq)] pub struct NetworkGraph { genesis_hash: BlockHash, channels: BTreeMap, @@ -335,7 +335,7 @@ where } } -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, Debug, PartialEq)] /// Details about one direction of a channel. Received /// within a channel update. pub struct DirectionalChannelInfo { @@ -376,7 +376,7 @@ impl_writeable!(DirectionalChannelInfo, 0, { last_update_message }); -#[derive(PartialEq)] +#[derive(Clone, Debug, PartialEq)] /// Details about a channel (both directions). /// Received within a channel announcement. pub struct ChannelInfo { @@ -447,7 +447,7 @@ impl Writeable for RoutingFees { } } -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, Debug, PartialEq)] /// Information received in the latest node_announcement from this node. pub struct NodeAnnouncementInfo { /// Protocol features the node announced support for @@ -513,7 +513,7 @@ impl Readable for NodeAnnouncementInfo { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] /// Details about a node in the network, known from the network announcement. pub struct NodeInfo { /// All valid channels a node has announced