Expose counterparty forwarding info in ChannelDetails.
authorValentine Wallace <vwallace@protonmail.com>
Fri, 12 Mar 2021 21:02:17 +0000 (16:02 -0500)
committerValentine Wallace <vwallace@protonmail.com>
Wed, 17 Mar 2021 21:36:26 +0000 (17:36 -0400)
Useful for constructing route hints for private channels in invoices.

Co-authored-by: Valentine Wallace <vwallace@protonmail.com>
Co-authored-by: Antoine Riard <ariard@student.42.fr>
fuzz/src/router.rs
lightning/src/ln/channel.rs
lightning/src/ln/channelmanager.rs
lightning/src/routing/router.rs

index 44c0b209c2cdd0d9cebcb9e413d1a7e763a3f519..e93618ca7137905ab7677a775e3697e390b6cd02 100644 (file)
@@ -212,6 +212,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
                                                                inbound_capacity_msat: 0,
                                                                is_live: true,
                                                                outbound_capacity_msat: 0,
+                                                               counterparty_forwarding_info: None,
                                                        });
                                                }
                                                Some(&first_hops_vec[..])
index ce048f0eeb96df1b429b066a852e42c93dea84dc..a6049e915e9fdc71de34cd62725743c6006ef6f1 100644 (file)
@@ -282,6 +282,7 @@ impl HTLCCandidate {
 }
 
 /// Information needed for constructing an invoice route hint for this channel.
+#[derive(Clone)]
 pub struct CounterpartyForwardingInfo {
        /// Base routing fee in millisatoshis.
        pub fee_base_msat: u32,
@@ -4132,6 +4133,11 @@ impl<Signer: Sign> Channel<Signer> {
                }
        }
 
+       /// Get forwarding information for the counterparty.
+       pub fn counterparty_forwarding_info(&self) -> Option<CounterpartyForwardingInfo> {
+               self.counterparty_forwarding_info.clone()
+       }
+
        pub fn channel_update(&mut self, msg: &msgs::ChannelUpdate) -> Result<(), ChannelError> {
                let usable_channel_value_msat = (self.channel_value_satoshis - self.counterparty_selected_channel_reserve_satoshis) * 1000;
                if msg.contents.htlc_minimum_msat >= usable_channel_value_msat {
@@ -4756,7 +4762,7 @@ mod tests {
        use ln::channel::{Channel,Sign,InboundHTLCOutput,OutboundHTLCOutput,InboundHTLCState,OutboundHTLCState,HTLCOutputInCommitment,HTLCCandidate,HTLCInitiator,TxCreationKeys};
        use ln::channel::MAX_FUNDING_SATOSHIS;
        use ln::features::InitFeatures;
-       use ln::msgs::{OptionalField, DataLossProtect, DecodeError};
+       use ln::msgs::{ChannelUpdate, DataLossProtect, DecodeError, OptionalField, UnsignedChannelUpdate};
        use ln::chan_utils;
        use ln::chan_utils::{ChannelPublicKeys, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT};
        use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
@@ -4767,6 +4773,7 @@ mod tests {
        use util::test_utils;
        use util::logger::Logger;
        use bitcoin::secp256k1::{Secp256k1, Message, Signature, All};
+       use bitcoin::secp256k1::ffi::Signature as FFISignature;
        use bitcoin::secp256k1::key::{SecretKey,PublicKey};
        use bitcoin::hashes::sha256::Hash as Sha256;
        use bitcoin::hashes::Hash;
@@ -5023,6 +5030,54 @@ mod tests {
                }
        }
 
+       #[test]
+       fn channel_update() {
+               let feeest = TestFeeEstimator{fee_est: 15000};
+               let secp_ctx = Secp256k1::new();
+               let seed = [42; 32];
+               let network = Network::Testnet;
+               let chain_hash = genesis_block(network).header.block_hash();
+               let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
+
+               // Create a channel.
+               let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+               let config = UserConfig::default();
+               let mut node_a_chan = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, node_b_node_id, 10000000, 100000, 42, &config).unwrap();
+               assert!(node_a_chan.counterparty_forwarding_info.is_none());
+               assert_eq!(node_a_chan.holder_htlc_minimum_msat, 1); // the default
+               assert!(node_a_chan.counterparty_forwarding_info().is_none());
+
+               // Make sure that receiving a channel update will update the Channel as expected.
+               let update = ChannelUpdate {
+                       contents: UnsignedChannelUpdate {
+                               chain_hash,
+                               short_channel_id: 0,
+                               timestamp: 0,
+                               flags: 0,
+                               cltv_expiry_delta: 100,
+                               htlc_minimum_msat: 5,
+                               htlc_maximum_msat: OptionalField::Absent,
+                               fee_base_msat: 110,
+                               fee_proportional_millionths: 11,
+                               excess_data: Vec::new(),
+                       },
+                       signature: Signature::from(unsafe { FFISignature::new() })
+               };
+               node_a_chan.channel_update(&update).unwrap();
+
+               // The counterparty can send an update with a higher minimum HTLC, but that shouldn't
+               // change our official htlc_minimum_msat.
+               assert_eq!(node_a_chan.holder_htlc_minimum_msat, 1);
+               match node_a_chan.counterparty_forwarding_info() {
+                       Some(info) => {
+                               assert_eq!(info.cltv_expiry_delta, 100);
+                               assert_eq!(info.fee_base_msat, 110);
+                               assert_eq!(info.fee_proportional_millionths, 11);
+                       },
+                       None => panic!("expected counterparty forwarding info to be Some")
+               }
+       }
+
        #[test]
        fn outbound_commitment_test() {
                // Test vectors from BOLT 3 Appendix C:
index 6702db0bd9a6387d05976953640dc75c585a34c0..02ac864137228ea855cc0c4da0576bb5574a4034 100644 (file)
@@ -39,6 +39,9 @@ use chain::Watch;
 use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
 use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, ChannelMonitorUpdateErr, HTLC_FAIL_BACK_BUFFER, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY, MonitorEvent, CLOSED_CHANNEL_UPDATE_ID};
 use chain::transaction::{OutPoint, TransactionData};
+// Since this struct is returned in `list_channels` methods, expose it here in case users want to
+// construct one themselves.
+pub use ln::channel::CounterpartyForwardingInfo;
 use ln::channel::{Channel, ChannelError};
 use ln::features::{InitFeatures, NodeFeatures};
 use routing::router::{Route, RouteHop};
@@ -574,6 +577,10 @@ pub struct ChannelDetails {
        /// True if the channel is (a) confirmed and funding_locked messages have been exchanged, (b)
        /// the peer is connected, and (c) no monitor update failure is pending resolution.
        pub is_live: bool,
+
+       /// Information on the fees and requirements that the counterparty requires when forwarding
+       /// payments to us through this channel.
+       pub counterparty_forwarding_info: Option<CounterpartyForwardingInfo>,
 }
 
 /// If a payment fails to send, it can be in one of several states. This enum is returned as the
@@ -892,6 +899,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                        outbound_capacity_msat,
                                        user_id: channel.get_user_id(),
                                        is_live: channel.is_live(),
+                                       counterparty_forwarding_info: channel.counterparty_forwarding_info(),
                                });
                        }
                }
index 682302481b653f47b1a5485d50dcbd015050f1ec..c9d7cc253217e28578ad0b26e58cef4665f7dbbe 100644 (file)
@@ -1473,6 +1473,7 @@ mod tests {
                        outbound_capacity_msat: 100000,
                        inbound_capacity_msat: 100000,
                        is_live: true,
+                       counterparty_forwarding_info: None,
                }];
 
                if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)) {
@@ -1790,6 +1791,7 @@ mod tests {
                        outbound_capacity_msat: 250_000_000,
                        inbound_capacity_msat: 0,
                        is_live: true,
+                       counterparty_forwarding_info: None,
                }];
                let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()),  &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
                assert_eq!(route.paths[0].len(), 2);
@@ -1837,6 +1839,7 @@ mod tests {
                        outbound_capacity_msat: 250_000_000,
                        inbound_capacity_msat: 0,
                        is_live: true,
+                       counterparty_forwarding_info: None,
                }];
                let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
                assert_eq!(route.paths[0].len(), 2);
@@ -1901,6 +1904,7 @@ mod tests {
                        outbound_capacity_msat: 250_000_000,
                        inbound_capacity_msat: 0,
                        is_live: true,
+                       counterparty_forwarding_info: None,
                }];
                let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
                assert_eq!(route.paths[0].len(), 2);
@@ -2037,6 +2041,7 @@ mod tests {
                        outbound_capacity_msat: 250_000_000,
                        inbound_capacity_msat: 0,
                        is_live: true,
+                       counterparty_forwarding_info: None,
                }];
                let mut last_hops = last_hops(&nodes);
                let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[6], None, Some(&our_chans.iter().collect::<Vec<_>>()), &last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)).unwrap();
@@ -2165,6 +2170,7 @@ mod tests {
                        outbound_capacity_msat: 100000,
                        inbound_capacity_msat: 100000,
                        is_live: true,
+                       counterparty_forwarding_info: None,
                }];
                let route = get_route(&source_node_id, &NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()), &target_node_id, None, Some(&our_chans.iter().collect::<Vec<_>>()), &last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::new(test_utils::TestLogger::new())).unwrap();
 
@@ -2296,6 +2302,7 @@ mod tests {
                        outbound_capacity_msat: 200_000_000,
                        inbound_capacity_msat: 0,
                        is_live: true,
+                       counterparty_forwarding_info: None,
                }];
 
                {