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>
inbound_capacity_msat: 0,
is_live: true,
outbound_capacity_msat: 0,
+ counterparty_forwarding_info: None,
});
}
Some(&first_hops_vec[..])
}
/// 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,
}
}
+ /// 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 {
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};
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;
}
}
+ #[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:
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};
/// 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
outbound_capacity_msat,
user_id: channel.get_user_id(),
is_live: channel.is_live(),
+ counterparty_forwarding_info: channel.counterparty_forwarding_info(),
});
}
}
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)) {
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);
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);
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);
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();
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();
outbound_capacity_msat: 200_000_000,
inbound_capacity_msat: 0,
is_live: true,
+ counterparty_forwarding_info: None,
}];
{