+ // Since nodes[1] should not have accepted the channel, it should
+ // not have generated any events.
+ assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+ }
+
+ #[test]
+ fn test_update_channel_config() {
+ let chanmon_cfg = create_chanmon_cfgs(2);
+ let node_cfg = create_node_cfgs(2, &chanmon_cfg);
+ let mut user_config = test_default_channel_config();
+ let node_chanmgr = create_node_chanmgrs(2, &node_cfg, &[Some(user_config), Some(user_config)]);
+ let nodes = create_network(2, &node_cfg, &node_chanmgr);
+ let _ = create_announced_chan_between_nodes(&nodes, 0, 1);
+ let channel = &nodes[0].node.list_channels()[0];
+
+ nodes[0].node.update_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &user_config.channel_config).unwrap();
+ let events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 0);
+
+ user_config.channel_config.forwarding_fee_base_msat += 10;
+ nodes[0].node.update_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &user_config.channel_config).unwrap();
+ assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().forwarding_fee_base_msat, user_config.channel_config.forwarding_fee_base_msat);
+ let events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ match &events[0] {
+ MessageSendEvent::BroadcastChannelUpdate { .. } => {},
+ _ => panic!("expected BroadcastChannelUpdate event"),
+ }
+
+ nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &ChannelConfigUpdate::default()).unwrap();
+ let events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 0);
+
+ let new_cltv_expiry_delta = user_config.channel_config.cltv_expiry_delta + 6;
+ nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &ChannelConfigUpdate {
+ cltv_expiry_delta: Some(new_cltv_expiry_delta),
+ ..Default::default()
+ }).unwrap();
+ assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().cltv_expiry_delta, new_cltv_expiry_delta);
+ let events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ match &events[0] {
+ MessageSendEvent::BroadcastChannelUpdate { .. } => {},
+ _ => panic!("expected BroadcastChannelUpdate event"),
+ }
+
+ let new_fee = user_config.channel_config.forwarding_fee_proportional_millionths + 100;
+ nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id], &ChannelConfigUpdate {
+ forwarding_fee_proportional_millionths: Some(new_fee),
+ ..Default::default()
+ }).unwrap();
+ assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().cltv_expiry_delta, new_cltv_expiry_delta);
+ assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().forwarding_fee_proportional_millionths, new_fee);
+ let events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ match &events[0] {
+ MessageSendEvent::BroadcastChannelUpdate { .. } => {},
+ _ => panic!("expected BroadcastChannelUpdate event"),
+ }
+
+ // If we provide a channel_id not associated with the peer, we should get an error and no updates
+ // should be applied to ensure update atomicity as specified in the API docs.
+ let bad_channel_id = ChannelId::v1_from_funding_txid(&[10; 32], 10);
+ let current_fee = nodes[0].node.list_channels()[0].config.unwrap().forwarding_fee_proportional_millionths;
+ let new_fee = current_fee + 100;
+ assert!(
+ matches!(
+ nodes[0].node.update_partial_channel_config(&channel.counterparty.node_id, &[channel.channel_id, bad_channel_id], &ChannelConfigUpdate {
+ forwarding_fee_proportional_millionths: Some(new_fee),
+ ..Default::default()
+ }),
+ Err(APIError::ChannelUnavailable { err: _ }),
+ )
+ );
+ // Check that the fee hasn't changed for the channel that exists.
+ assert_eq!(nodes[0].node.list_channels()[0].config.unwrap().forwarding_fee_proportional_millionths, current_fee);
+ let events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 0);
+ }
+
+ #[test]
+ fn test_payment_display() {
+ let payment_id = PaymentId([42; 32]);
+ assert_eq!(format!("{}", &payment_id), "2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a");
+ let payment_hash = PaymentHash([42; 32]);
+ assert_eq!(format!("{}", &payment_hash), "2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a");
+ let payment_preimage = PaymentPreimage([42; 32]);
+ assert_eq!(format!("{}", &payment_preimage), "2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a");
+ }
+
+ #[test]
+ fn test_trigger_lnd_force_close() {
+ let chanmon_cfg = create_chanmon_cfgs(2);
+ let node_cfg = create_node_cfgs(2, &chanmon_cfg);
+ let user_config = test_default_channel_config();
+ let node_chanmgr = create_node_chanmgrs(2, &node_cfg, &[Some(user_config), Some(user_config)]);
+ let nodes = create_network(2, &node_cfg, &node_chanmgr);
+
+ // Open a channel, immediately disconnect each other, and broadcast Alice's latest state.
+ let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
+ nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
+ nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
+ nodes[0].node.force_close_broadcasting_latest_txn(&chan_id, &nodes[1].node.get_our_node_id()).unwrap();
+ check_closed_broadcast(&nodes[0], 1, true);
+ check_added_monitors(&nodes[0], 1);
+ check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 100000);
+ {
+ let txn = nodes[0].tx_broadcaster.txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ check_spends!(txn[0], funding_tx);
+ }
+
+ // Since they're disconnected, Bob won't receive Alice's `Error` message. Reconnect them
+ // such that Bob sends a `ChannelReestablish` to Alice since the channel is still open from
+ // their side.
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init {
+ features: nodes[1].node.init_features(), networks: None, remote_network_address: None
+ }, true).unwrap();
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init {
+ features: nodes[0].node.init_features(), networks: None, remote_network_address: None
+ }, false).unwrap();
+ assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
+ let channel_reestablish = get_event_msg!(
+ nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id()
+ );
+ nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &channel_reestablish);
+
+ // Alice should respond with an error since the channel isn't known, but a bogus
+ // `ChannelReestablish` should be sent first, such that we actually trigger Bob to force
+ // close even if it was an lnd node.
+ let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(msg_events.len(), 2);
+ if let MessageSendEvent::SendChannelReestablish { node_id, msg } = &msg_events[0] {
+ assert_eq!(*node_id, nodes[1].node.get_our_node_id());
+ assert_eq!(msg.next_local_commitment_number, 0);
+ assert_eq!(msg.next_remote_commitment_number, 0);
+ nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &msg);
+ } else { panic!() };
+ check_closed_broadcast(&nodes[1], 1, true);
+ check_added_monitors(&nodes[1], 1);
+ let expected_close_reason = ClosureReason::ProcessingError {
+ err: "Peer sent an invalid channel_reestablish to force close in a non-standard way".to_string()
+ };
+ check_closed_event!(nodes[1], 1, expected_close_reason, [nodes[0].node.get_our_node_id()], 100000);
+ {
+ let txn = nodes[1].tx_broadcaster.txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ check_spends!(txn[0], funding_tx);
+ }