if channel_type.supports_any_optional_bits() {
return Err(ChannelError::Close("Channel Type field contained optional bits - this is not allowed".to_owned()));
}
- // We currently only allow two channel types, so write it all out here - we allow
- // `only_static_remote_key` in all contexts, and further allow
- // `static_remote_key|scid_privacy` if the channel is not publicly announced.
- let mut allowed_type = ChannelTypeFeatures::only_static_remote_key();
- if *channel_type != allowed_type {
- allowed_type.set_scid_privacy_required();
- if *channel_type != allowed_type {
+
+ if channel_type.requires_unknown_bits() {
+ return Err(ChannelError::Close("Channel Type field contains unknown bits".to_owned()));
+ }
+
+ // We currently only allow four channel types, so write it all out here - we allow
+ // `only_static_remote_key` or `static_remote_key | zero_conf` in all contexts, and
+ // further allow `static_remote_key | scid_privacy` or
+ // `static_remote_key | scid_privacy | zero_conf`, if the channel is not
+ // publicly announced.
+ if *channel_type != ChannelTypeFeatures::only_static_remote_key() {
+ if !channel_type.requires_scid_privacy() && !channel_type.requires_zero_conf() {
return Err(ChannelError::Close("Channel Type was not understood".to_owned()));
}
- if announced_channel {
+
+ if channel_type.requires_scid_privacy() && announced_channel {
return Err(ChannelError::Close("SCID Alias/Privacy Channel Type cannot be set on a public channel".to_owned()));
}
}
use ln::channelmanager::{HTLCSource, PaymentId};
use ln::channel::{Channel, InboundHTLCOutput, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator};
use ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS};
- use ln::features::InitFeatures;
+ use ln::features::{InitFeatures, ChannelTypeFeatures};
use ln::msgs::{ChannelUpdate, DataLossProtect, DecodeError, OptionalField, UnsignedChannelUpdate};
use ln::script::ShutdownScript;
use ln::chan_utils;
assert_eq!(chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_secret, &base_secret).unwrap(),
SecretKey::from_slice(&hex::decode("d09ffff62ddb2297ab000cc85bcb4283fdeb6aa052affbc9dddcf33b61078110").unwrap()[..]).unwrap());
}
+
+ #[test]
+ fn test_zero_conf_channel_type_support() {
+ let feeest = TestFeeEstimator{fee_est: 15000};
+ let secp_ctx = Secp256k1::new();
+ let seed = [42; 32];
+ let network = Network::Testnet;
+ let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
+ let logger = test_utils::TestLogger::new();
+
+ let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+ let config = UserConfig::default();
+ let node_a_chan = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider,
+ node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).unwrap();
+
+ let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key();
+ channel_type_features.set_zero_conf_required();
+
+ let mut open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
+ open_channel_msg.channel_type = Some(channel_type_features);
+ let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
+ let res = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider,
+ node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger, 42);
+ assert!(res.is_ok());
+ }
}
/// [`Event::ChannelClosed::user_channel_id`] to allow tracking of which events correspond
/// with which `accept_inbound_channel`/`accept_inbound_channel_from_trusted_peer_0conf` call.
///
+ /// Note that this method will return an error and reject the channel, if it requires support
+ /// for zero confirmations. Instead, `accept_inbound_channel_from_trusted_peer_0conf` must be
+ /// used to accept such channels.
+ ///
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
pub fn accept_inbound_channel(&self, temporary_channel_id: &[u8; 32], counterparty_node_id: &PublicKey, user_channel_id: u64) -> Result<(), APIError> {
if *counterparty_node_id != channel.get().get_counterparty_node_id() {
return Err(APIError::APIMisuseError { err: "The passed counterparty_node_id doesn't match the channel's counterparty node_id".to_owned() });
}
- if accept_0conf { channel.get_mut().set_0conf(); }
+ if accept_0conf {
+ channel.get_mut().set_0conf();
+ } else if channel.get().get_channel_type().requires_zero_conf() {
+ let send_msg_err_event = events::MessageSendEvent::HandleError {
+ node_id: channel.get().get_counterparty_node_id(),
+ action: msgs::ErrorAction::SendErrorMessage{
+ msg: msgs::ErrorMessage { channel_id: temporary_channel_id.clone(), data: "No zero confirmation channels accepted".to_owned(), }
+ }
+ };
+ channel_state.pending_msg_events.push(send_msg_err_event);
+ let _ = remove_channel!(self, channel_state, channel);
+ return Err(APIError::APIMisuseError { err: "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned() });
+ }
+
channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
node_id: channel.get().get_counterparty_node_id(),
msg: channel.get_mut().accept_inbound_channel(user_channel_id),
},
hash_map::Entry::Vacant(entry) => {
if !self.default_configuration.manually_accept_inbound_channels {
+ if channel.get_channel_type().requires_zero_conf() {
+ return Err(MsgHandleErrInternal::send_err_msg_no_close("No zero confirmation channels accepted".to_owned(), msg.temporary_channel_id.clone()));
+ }
channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
node_id: counterparty_node_id.clone(),
msg: channel.accept_inbound_channel(0),
,
// Byte 5
,
+ // Byte 6
+ ,
],
optional_features: [
// Byte 0
,
// Byte 5
ChannelType | SCIDPrivacy,
+ // Byte 6
+ ZeroConf,
],
});
define_context!(NodeContext {
// Byte 5
ChannelType | SCIDPrivacy,
// Byte 6
- Keysend,
+ ZeroConf | Keysend,
],
});
define_context!(ChannelContext {
,
// Byte 5
SCIDPrivacy,
+ // Byte 6
+ ZeroConf,
],
optional_features: [
// Byte 0
,
// Byte 5
,
+ // Byte 6
+ ,
],
});
define_feature!(47, SCIDPrivacy, [InitContext, NodeContext, ChannelTypeContext],
"Feature flags for only forwarding with SCID aliasing. Called `option_scid_alias` in the BOLTs",
set_scid_privacy_optional, set_scid_privacy_required, supports_scid_privacy, requires_scid_privacy);
-
+ define_feature!(51, ZeroConf, [InitContext, NodeContext, ChannelTypeContext],
+ "Feature flags for accepting channels with zero confirmations. Called `option_zeroconf` in the BOLTs",
+ set_zero_conf_optional, set_zero_conf_required, supports_zero_conf, requires_zero_conf);
define_feature!(55, Keysend, [NodeContext],
"Feature flags for keysend payments.", set_keysend_optional, set_keysend_required,
supports_keysend, requires_keysend);
assert!(InitFeatures::known().supports_scid_privacy());
assert!(NodeFeatures::known().supports_scid_privacy());
+ assert!(ChannelTypeFeatures::known().supports_scid_privacy());
assert!(!InitFeatures::known().requires_scid_privacy());
assert!(!NodeFeatures::known().requires_scid_privacy());
+ assert!(ChannelTypeFeatures::known().requires_scid_privacy());
assert!(InitFeatures::known().supports_wumbo());
assert!(NodeFeatures::known().supports_wumbo());
assert!(!InitFeatures::known().requires_wumbo());
assert!(!NodeFeatures::known().requires_wumbo());
+ assert!(InitFeatures::known().supports_zero_conf());
+ assert!(!InitFeatures::known().requires_zero_conf());
+ assert!(NodeFeatures::known().supports_zero_conf());
+ assert!(!NodeFeatures::known().requires_zero_conf());
+ assert!(ChannelTypeFeatures::known().supports_zero_conf());
+ assert!(ChannelTypeFeatures::known().requires_zero_conf());
+
let mut init_features = InitFeatures::known();
assert!(init_features.initial_routing_sync());
init_features.clear_initial_routing_sync();
// - opt_shutdown_anysegwit
// -
// - option_channel_type | option_scid_alias
- assert_eq!(node_features.flags.len(), 6);
+ // - option_zeroconf
+ assert_eq!(node_features.flags.len(), 7);
assert_eq!(node_features.flags[0], 0b00000010);
assert_eq!(node_features.flags[1], 0b01010001);
assert_eq!(node_features.flags[2], 0b00001010);
assert_eq!(node_features.flags[3], 0b00001000);
assert_eq!(node_features.flags[4], 0b00000000);
assert_eq!(node_features.flags[5], 0b10100000);
+ assert_eq!(node_features.flags[6], 0b00001000);
}
// Check that cleared flags are kept blank when converting back:
use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, MIN_CLTV_EXPIRY_DELTA};
use routing::network_graph::RoutingFees;
use routing::router::{PaymentParameters, RouteHint, RouteHintHop};
-use ln::features::{InitFeatures, InvoiceFeatures};
+use ln::features::{InitFeatures, InvoiceFeatures, ChannelTypeFeatures};
use ln::msgs;
-use ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, OptionalField, ChannelUpdate};
+use ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, OptionalField, ChannelUpdate, ErrorAction};
use ln::wire::Encode;
use util::enforcing_trait_impls::EnforcingSigner;
use util::events::{ClosureReason, Event, MessageSendEvent, MessageSendEventsProvider};
});
check_closed_broadcast!(nodes[1], true);
}
+
+#[test]
+fn test_zero_conf_accept_reject() {
+ let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key();
+ channel_type_features.set_zero_conf_required();
+
+ // 1. Check we reject zero conf channels by default
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None).unwrap();
+ let mut open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+
+ open_channel_msg.channel_type = Some(channel_type_features.clone());
+
+ nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &open_channel_msg);
+
+ let msg_events = nodes[1].node.get_and_clear_pending_msg_events();
+ match msg_events[0] {
+ MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg, .. }, .. } => {
+ assert_eq!(msg.data, "No zero confirmation channels accepted".to_owned());
+ },
+ _ => panic!(),
+ }
+
+ // 2. Check we can manually accept zero conf channels via the right method
+ let mut manually_accept_conf = UserConfig::default();
+ manually_accept_conf.manually_accept_inbound_channels = true;
+
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs,
+ &[None, Some(manually_accept_conf.clone())]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ // 2.1 First try the non-0conf method to manually accept
+ nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42,
+ Some(manually_accept_conf)).unwrap();
+ let mut open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel,
+ nodes[1].node.get_our_node_id());
+
+ open_channel_msg.channel_type = Some(channel_type_features.clone());
+
+ nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(),
+ &open_channel_msg);
+
+ // Assert that `nodes[1]` has no `MessageSendEvent::SendAcceptChannel` in the `msg_events`.
+ assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
+
+ let events = nodes[1].node.get_and_clear_pending_events();
+
+ match events[0] {
+ Event::OpenChannelRequest { temporary_channel_id, .. } => {
+ // Assert we fail to accept via the non-0conf method
+ assert!(nodes[1].node.accept_inbound_channel(&temporary_channel_id,
+ &nodes[0].node.get_our_node_id(), 0).is_err());
+ },
+ _ => panic!(),
+ }
+
+ let msg_events = nodes[1].node.get_and_clear_pending_msg_events();
+ match msg_events[0] {
+ MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg, .. }, .. } => {
+ assert_eq!(msg.data, "No zero confirmation channels accepted".to_owned());
+ },
+ _ => panic!(),
+ }
+
+ // 2.2 Try again with the 0conf method to manually accept
+ nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42,
+ Some(manually_accept_conf)).unwrap();
+ let mut open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel,
+ nodes[1].node.get_our_node_id());
+
+ open_channel_msg.channel_type = Some(channel_type_features);
+
+ nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(),
+ &open_channel_msg);
+
+ let events = nodes[1].node.get_and_clear_pending_events();
+
+ match events[0] {
+ Event::OpenChannelRequest { temporary_channel_id, .. } => {
+ // Assert we can accept via the 0conf method
+ assert!(nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(
+ &temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).is_ok());
+ },
+ _ => panic!(),
+ }
+
+ // Check we would send accept
+ let msg_events = nodes[1].node.get_and_clear_pending_msg_events();
+ match msg_events[0] {
+ MessageSendEvent::SendAcceptChannel { .. } => {},
+ _ => panic!(),
+ }
+}
/// transaction.
claim_from_onchain_tx: bool,
},
- /// Used to indicate that a channel with the given `channel_id` is in the process of closure.
+ /// Used to indicate that a previously opened channel with the given `channel_id` is in the
+ /// process of closure.
ChannelClosed {
/// The channel_id of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
/// the resulting [`ChannelManager`] will not be readable by versions of LDK prior to
/// 0.0.106.
///
+ /// Furthermore, note that if [`ChannelTypeFeatures::supports_zero_conf`] returns true on this type,
+ /// the resulting [`ChannelManager`] will not be readable by versions of LDK prior to
+ /// 0.0.107. Channels setting this type also need to get manually accepted via
+ /// [`crate::ln::channelmanager::ChannelManager::accept_inbound_channel_from_trusted_peer_0conf`],
+ /// or will be rejected otherwise.
+ ///
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
channel_type: ChannelTypeFeatures,
},