Skip to content

Commit 6ec1d0a

Browse files
committed
Implement ZeroConf channel type.
1 parent ce7b0b4 commit 6ec1d0a

File tree

5 files changed

+178
-13
lines changed

5 files changed

+178
-13
lines changed

lightning/src/ln/channel.rs

+40-9
Original file line numberDiff line numberDiff line change
@@ -1081,16 +1081,22 @@ impl<Signer: Sign> Channel<Signer> {
10811081
if channel_type.supports_any_optional_bits() {
10821082
return Err(ChannelError::Close("Channel Type field contained optional bits - this is not allowed".to_owned()));
10831083
}
1084-
// We currently only allow two channel types, so write it all out here - we allow
1085-
// `only_static_remote_key` in all contexts, and further allow
1086-
// `static_remote_key|scid_privacy` if the channel is not publicly announced.
1087-
let mut allowed_type = ChannelTypeFeatures::only_static_remote_key();
1088-
if *channel_type != allowed_type {
1089-
allowed_type.set_scid_privacy_required();
1090-
if *channel_type != allowed_type {
1084+
1085+
if channel_type.requires_unknown_bits() {
1086+
return Err(ChannelError::Close("Channel Type field contains unknown bits".to_owned()));
1087+
}
1088+
1089+
// We currently only allow four channel types, so write it all out here - we allow
1090+
// `only_static_remote_key` or `static_remote_key | zero_conf` in all contexts, and
1091+
// further allow `static_remote_key | scid_privacy` or
1092+
// `static_remote_key | scid_privacy | zero_conf`, if the channel is not
1093+
// publicly announced.
1094+
if *channel_type != ChannelTypeFeatures::only_static_remote_key() {
1095+
if !channel_type.requires_scid_privacy() && !channel_type.requires_zero_conf() {
10911096
return Err(ChannelError::Close("Channel Type was not understood".to_owned()));
10921097
}
1093-
if announced_channel {
1098+
1099+
if channel_type.requires_scid_privacy() && announced_channel {
10941100
return Err(ChannelError::Close("SCID Alias/Privacy Channel Type cannot be set on a public channel".to_owned()));
10951101
}
10961102
}
@@ -6407,7 +6413,7 @@ mod tests {
64076413
use ln::channelmanager::{HTLCSource, PaymentId};
64086414
use ln::channel::{Channel, InboundHTLCOutput, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator};
64096415
use ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS};
6410-
use ln::features::InitFeatures;
6416+
use ln::features::{InitFeatures, ChannelTypeFeatures};
64116417
use ln::msgs::{ChannelUpdate, DataLossProtect, DecodeError, OptionalField, UnsignedChannelUpdate};
64126418
use ln::script::ShutdownScript;
64136419
use ln::chan_utils;
@@ -7722,4 +7728,29 @@ mod tests {
77227728
assert_eq!(chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_secret, &base_secret).unwrap(),
77237729
SecretKey::from_slice(&hex::decode("d09ffff62ddb2297ab000cc85bcb4283fdeb6aa052affbc9dddcf33b61078110").unwrap()[..]).unwrap());
77247730
}
7731+
7732+
#[test]
7733+
fn test_zero_conf_channel_type_support() {
7734+
let feeest = TestFeeEstimator{fee_est: 15000};
7735+
let secp_ctx = Secp256k1::new();
7736+
let seed = [42; 32];
7737+
let network = Network::Testnet;
7738+
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
7739+
let logger = test_utils::TestLogger::new();
7740+
7741+
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
7742+
let config = UserConfig::default();
7743+
let node_a_chan = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider,
7744+
node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).unwrap();
7745+
7746+
let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key();
7747+
channel_type_features.set_zero_conf_required();
7748+
7749+
let mut open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
7750+
open_channel_msg.channel_type = Some(channel_type_features);
7751+
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
7752+
let res = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider,
7753+
node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger, 42);
7754+
assert!(res.is_ok());
7755+
}
77257756
}

lightning/src/ln/channelmanager.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -4159,6 +4159,10 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
41594159
/// [`Event::ChannelClosed::user_channel_id`] to allow tracking of which events correspond
41604160
/// with which `accept_inbound_channel`/`accept_inbound_channel_from_trusted_peer_0conf` call.
41614161
///
4162+
/// Note that this method will return an error and reject the channel, if it requires support
4163+
/// for zero confirmations. Instead, `accept_inbound_channel_from_trusted_peer_0conf` must be
4164+
/// used to accept such channels.
4165+
///
41624166
/// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
41634167
/// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
41644168
pub fn accept_inbound_channel(&self, temporary_channel_id: &[u8; 32], counterparty_node_id: &PublicKey, user_channel_id: u64) -> Result<(), APIError> {
@@ -4200,7 +4204,20 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
42004204
if *counterparty_node_id != channel.get().get_counterparty_node_id() {
42014205
return Err(APIError::APIMisuseError { err: "The passed counterparty_node_id doesn't match the channel's counterparty node_id".to_owned() });
42024206
}
4203-
if accept_0conf { channel.get_mut().set_0conf(); }
4207+
if accept_0conf {
4208+
channel.get_mut().set_0conf();
4209+
} else if channel.get().get_channel_type().requires_zero_conf() {
4210+
let send_msg_err_event = events::MessageSendEvent::HandleError {
4211+
node_id: channel.get().get_counterparty_node_id(),
4212+
action: msgs::ErrorAction::SendErrorMessage{
4213+
msg: msgs::ErrorMessage { channel_id: temporary_channel_id.clone(), data: "No zero confirmation channels accepted".to_owned(), }
4214+
}
4215+
};
4216+
channel_state.pending_msg_events.push(send_msg_err_event);
4217+
let _ = remove_channel!(self, channel_state, channel);
4218+
return Err(APIError::APIMisuseError { err: "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned() });
4219+
}
4220+
42044221
channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
42054222
node_id: channel.get().get_counterparty_node_id(),
42064223
msg: channel.get_mut().accept_inbound_channel(user_channel_id),
@@ -4242,6 +4259,9 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
42424259
},
42434260
hash_map::Entry::Vacant(entry) => {
42444261
if !self.default_configuration.manually_accept_inbound_channels {
4262+
if channel.get_channel_type().requires_zero_conf() {
4263+
return Err(MsgHandleErrInternal::send_err_msg_no_close("No zero confirmation channels accepted".to_owned(), msg.temporary_channel_id.clone()));
4264+
}
42454265
channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
42464266
node_id: counterparty_node_id.clone(),
42474267
msg: channel.accept_inbound_channel(0),

lightning/src/ln/features.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ mod sealed {
218218
,
219219
// Byte 5
220220
SCIDPrivacy,
221+
// Byte 6
222+
ZeroConf,
221223
],
222224
optional_features: [
223225
// Byte 0
@@ -232,6 +234,8 @@ mod sealed {
232234
,
233235
// Byte 5
234236
,
237+
// Byte 6
238+
,
235239
],
236240
});
237241

@@ -402,7 +406,9 @@ mod sealed {
402406
define_feature!(47, SCIDPrivacy, [InitContext, NodeContext, ChannelTypeContext],
403407
"Feature flags for only forwarding with SCID aliasing. Called `option_scid_alias` in the BOLTs",
404408
set_scid_privacy_optional, set_scid_privacy_required, supports_scid_privacy, requires_scid_privacy);
405-
409+
define_feature!(51, ZeroConf, [ChannelTypeContext],
410+
"Feature flags for accepting channels with zero confirmations. Called `option_zeroconf` in the BOLTs",
411+
set_zero_conf_optional, set_zero_conf_required, supports_zero_conf, requires_zero_conf);
406412
define_feature!(55, Keysend, [NodeContext],
407413
"Feature flags for keysend payments.", set_keysend_optional, set_keysend_required,
408414
supports_keysend, requires_keysend);
@@ -852,14 +858,19 @@ mod tests {
852858

853859
assert!(InitFeatures::known().supports_scid_privacy());
854860
assert!(NodeFeatures::known().supports_scid_privacy());
861+
assert!(ChannelTypeFeatures::known().supports_scid_privacy());
855862
assert!(!InitFeatures::known().requires_scid_privacy());
856863
assert!(!NodeFeatures::known().requires_scid_privacy());
864+
assert!(ChannelTypeFeatures::known().requires_scid_privacy());
857865

858866
assert!(InitFeatures::known().supports_wumbo());
859867
assert!(NodeFeatures::known().supports_wumbo());
860868
assert!(!InitFeatures::known().requires_wumbo());
861869
assert!(!NodeFeatures::known().requires_wumbo());
862870

871+
assert!(ChannelTypeFeatures::known().supports_zero_conf());
872+
assert!(ChannelTypeFeatures::known().requires_zero_conf());
873+
863874
let mut init_features = InitFeatures::known();
864875
assert!(init_features.initial_routing_sync());
865876
init_features.clear_initial_routing_sync();

lightning/src/ln/functional_tests.rs

+97-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use ln::channel::{Channel, ChannelError};
2424
use ln::{chan_utils, onion_utils};
2525
use ln::chan_utils::{htlc_success_tx_weight, htlc_timeout_tx_weight, HTLCOutputInCommitment};
2626
use routing::router::{PaymentParameters, Route, RouteHop, RouteParameters, find_route, get_route};
27-
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
27+
use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
2828
use ln::msgs;
2929
use ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, OptionalField, ErrorAction};
3030
use util::enforcing_trait_impls::EnforcingSigner;
@@ -10057,3 +10057,99 @@ fn test_max_dust_htlc_exposure() {
1005710057
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false);
1005810058
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, true);
1005910059
}
10060+
10061+
#[test]
10062+
fn test_zero_conf_accept_reject() {
10063+
let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key();
10064+
channel_type_features.set_zero_conf_required();
10065+
10066+
// 1. Check we reject zero conf channels by default
10067+
let chanmon_cfgs = create_chanmon_cfgs(2);
10068+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
10069+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
10070+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
10071+
10072+
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None).unwrap();
10073+
let mut open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
10074+
10075+
open_channel_msg.channel_type = Some(channel_type_features.clone());
10076+
10077+
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &open_channel_msg);
10078+
10079+
let msg_events = nodes[1].node.get_and_clear_pending_msg_events();
10080+
match msg_events[0] {
10081+
MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg, .. }, .. } => {
10082+
assert_eq!(msg.data, "No zero confirmation channels accepted".to_owned());
10083+
},
10084+
_ => panic!(),
10085+
}
10086+
10087+
// 2. Check we can manually accept zero conf channels via the right method
10088+
let mut manually_accept_conf = UserConfig::default();
10089+
manually_accept_conf.manually_accept_inbound_channels = true;
10090+
10091+
let chanmon_cfgs = create_chanmon_cfgs(2);
10092+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
10093+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs,
10094+
&[None, Some(manually_accept_conf.clone())]);
10095+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
10096+
10097+
// 2.1 First try the non-0conf method to manually accept
10098+
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42,
10099+
Some(manually_accept_conf)).unwrap();
10100+
let mut open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel,
10101+
nodes[1].node.get_our_node_id());
10102+
10103+
open_channel_msg.channel_type = Some(channel_type_features.clone());
10104+
10105+
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(),
10106+
&open_channel_msg);
10107+
10108+
// Assert that `nodes[1]` has no `MessageSendEvent::SendAcceptChannel` in the `msg_events`.
10109+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
10110+
10111+
let events = nodes[1].node.get_and_clear_pending_events();
10112+
10113+
match events[0] {
10114+
Event::OpenChannelRequest { temporary_channel_id, .. } => {
10115+
// Assert we fail to accept via the non-0conf method
10116+
assert!(nodes[1].node.accept_inbound_channel(&temporary_channel_id,
10117+
&nodes[0].node.get_our_node_id(), 0).is_err());
10118+
},
10119+
_ => panic!(),
10120+
}
10121+
10122+
let msg_events = nodes[1].node.get_and_clear_pending_msg_events();
10123+
match msg_events[0] {
10124+
MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg, .. }, .. } => {
10125+
assert_eq!(msg.data, "No zero confirmation channels accepted".to_owned());
10126+
},
10127+
_ => panic!(),
10128+
}
10129+
10130+
// 2.2 Try again with the 0conf method to manually accept
10131+
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42,
10132+
Some(manually_accept_conf)).unwrap();
10133+
let mut open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel,
10134+
nodes[1].node.get_our_node_id());
10135+
10136+
open_channel_msg.channel_type = Some(channel_type_features);
10137+
10138+
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(),
10139+
&open_channel_msg);
10140+
10141+
let events = nodes[1].node.get_and_clear_pending_events();
10142+
10143+
match events[0] {
10144+
Event::OpenChannelRequest { temporary_channel_id, .. } => {
10145+
// Assert we can accept via the 0conf method
10146+
assert!(nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(
10147+
&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).is_ok());
10148+
},
10149+
_ => panic!(),
10150+
}
10151+
10152+
// Don't handle remaining events
10153+
nodes[1].node.get_and_clear_pending_msg_events();
10154+
nodes[1].node.get_and_clear_pending_events();
10155+
}

lightning/src/util/events.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,8 @@ pub enum Event {
397397
/// transaction.
398398
claim_from_onchain_tx: bool,
399399
},
400-
/// Used to indicate that a channel with the given `channel_id` is in the process of closure.
400+
/// Used to indicate that a previously opened channel with the given `channel_id` is in the
401+
/// process of closure.
401402
ChannelClosed {
402403
/// The channel_id of the channel which has been closed. Note that on-chain transactions
403404
/// resolving the channel are likely still awaiting confirmation.
@@ -466,6 +467,12 @@ pub enum Event {
466467
/// the resulting [`ChannelManager`] will not be readable by versions of LDK prior to
467468
/// 0.0.106.
468469
///
470+
/// Furthermore, note that if [`ChannelTypeFeatures::supports_zero_conf`] returns true on this type,
471+
/// the resulting [`ChannelManager`] will not be readable by versions of LDK prior to
472+
/// 0.0.107. Channels setting this type also need to get manually accepted via
473+
/// [`crate::ln::channelmanager::ChannelManager::accept_inbound_channel_from_trusted_peer_0conf`],
474+
/// or will be rejected otherwise.
475+
///
469476
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
470477
channel_type: ChannelTypeFeatures,
471478
},

0 commit comments

Comments
 (0)