Skip to content

Commit

Permalink
Implement ZeroConf channel type.
Browse files Browse the repository at this point in the history
  • Loading branch information
tnull committed Jun 1, 2022
1 parent ce7b0b4 commit cfab760
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 12 deletions.
49 changes: 40 additions & 9 deletions lightning/src/ln/channel.rs
Expand Up @@ -1081,16 +1081,22 @@ impl<Signer: Sign> Channel<Signer> {
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()));
}
}
Expand Down Expand Up @@ -6407,7 +6413,7 @@ mod tests {
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;
Expand Down Expand Up @@ -7722,4 +7728,29 @@ mod tests {
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());
}
}
9 changes: 8 additions & 1 deletion lightning/src/ln/channelmanager.rs
Expand Up @@ -4200,7 +4200,11 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
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() {
return Err(APIError::APIMisuseError { err: "This channel requires 0conf. Please use accept_inbound_channel_from_trusted_peer_0conf to accept.".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),
Expand Down Expand Up @@ -4242,6 +4246,9 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
},
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),
Expand Down
13 changes: 12 additions & 1 deletion lightning/src/ln/features.rs
Expand Up @@ -218,6 +218,8 @@ mod sealed {
,
// Byte 5
SCIDPrivacy,
// Byte 6
ZeroConf,
],
optional_features: [
// Byte 0
Expand All @@ -232,6 +234,8 @@ mod sealed {
,
// Byte 5
,
// Byte 6
,
],
});

Expand Down Expand Up @@ -402,7 +406,9 @@ mod sealed {
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, [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);
Expand Down Expand Up @@ -852,14 +858,19 @@ mod tests {

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!(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();
Expand Down
70 changes: 69 additions & 1 deletion lightning/src/ln/functional_tests.rs
Expand Up @@ -24,7 +24,7 @@ use ln::channel::{Channel, ChannelError};
use ln::{chan_utils, onion_utils};
use ln::chan_utils::{htlc_success_tx_weight, htlc_timeout_tx_weight, HTLCOutputInCommitment};
use routing::router::{PaymentParameters, Route, RouteHop, RouteParameters, find_route, get_route};
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
use ln::msgs;
use ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, OptionalField, ErrorAction};
use util::enforcing_trait_impls::EnforcingSigner;
Expand Down Expand Up @@ -10057,3 +10057,71 @@ fn test_max_dust_htlc_exposure() {
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false);
do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, 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();

// 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 events = nodes[1].node.get_and_clear_pending_msg_events();
match events[0] {
MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg, .. }, .. } => {
assert_eq!(msg.data, "No zero confirmation channels accepted".to_owned());
},
_ => panic!(),
}

// Check we can manually accept zero conf channels
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);

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);

// 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());
// 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!(),
}

// Don't handle generated events
nodes[1].node.get_and_clear_pending_msg_events();
nodes[1].node.get_and_clear_pending_events();
}
6 changes: 6 additions & 0 deletions lightning/src/util/events.rs
Expand Up @@ -466,6 +466,12 @@ pub enum Event {
/// 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,
},
Expand Down

0 comments on commit cfab760

Please sign in to comment.