Skip to content

Fix bug in check_spend_remote_htlc and let csv delays being user configurable #355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use secp256k1;
use ln::msgs;
use ln::msgs::{DecodeError, OptionalField, LocalFeatures};
use ln::channelmonitor::ChannelMonitor;
use ln::channelmanager::{PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingForwardHTLCInfo, RAACommitmentOrder, PaymentPreimage, PaymentHash};
use ln::channelmanager::{PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingForwardHTLCInfo, RAACommitmentOrder, PaymentPreimage, PaymentHash, BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT};
use ln::chan_utils::{TxCreationKeys,HTLCOutputInCommitment,HTLC_SUCCESS_TX_WEIGHT,HTLC_TIMEOUT_TX_WEIGHT};
use ln::chan_utils;
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
Expand Down Expand Up @@ -317,7 +317,7 @@ pub(super) struct Channel {
their_htlc_minimum_msat: u64,
our_htlc_minimum_msat: u64,
their_to_self_delay: u16,
//implied by BREAKDOWN_TIMEOUT: our_to_self_delay: u16,
our_to_self_delay: u16,
#[cfg(test)]
pub their_max_accepted_htlcs: u16,
#[cfg(not(test))]
Expand Down Expand Up @@ -347,14 +347,6 @@ pub const OUR_MAX_HTLCS: u16 = 50; //TODO
/// on ice until the funding transaction gets more confirmations, but the LN protocol doesn't
/// really allow for this, so instead we're stuck closing it out at that point.
const UNCONF_THRESHOLD: u32 = 6;
/// The amount of time we require our counterparty wait to claim their money (ie time between when
/// we, or our watchtower, must check for them having broadcast a theft transaction).
#[cfg(not(test))]
const BREAKDOWN_TIMEOUT: u16 = 6 * 24 * 7; //TODO?
#[cfg(test)]
pub const BREAKDOWN_TIMEOUT: u16 = 6 * 24 * 7; //TODO?
/// The amount of time we're willing to wait to claim money back to us
const MAX_LOCAL_BREAKDOWN_TIMEOUT: u16 = 6 * 24 * 14;
/// Exposing these two constants for use in test in ChannelMonitor
pub const COMMITMENT_TX_BASE_WEIGHT: u64 = 724;
pub const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
Expand Down Expand Up @@ -421,6 +413,9 @@ impl Channel {
if push_msat > channel_value_satoshis * 1000 {
return Err(APIError::APIMisuseError{err: "push value > channel value"});
}
if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
return Err(APIError::APIMisuseError{err: "Configured with an unreasonable our_to_self_delay putting user funds at risks"});
}


let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
Expand All @@ -432,7 +427,7 @@ impl Channel {

let secp_ctx = Secp256k1::new();
let channel_monitor = ChannelMonitor::new(&chan_keys.revocation_base_key, &chan_keys.delayed_payment_base_key,
&chan_keys.htlc_base_key, &chan_keys.payment_base_key, &keys_provider.get_shutdown_pubkey(), BREAKDOWN_TIMEOUT,
&chan_keys.htlc_base_key, &chan_keys.payment_base_key, &keys_provider.get_shutdown_pubkey(), config.own_channel_config.our_to_self_delay,
keys_provider.get_destination_script(), logger.clone());

Ok(Channel {
Expand Down Expand Up @@ -489,6 +484,7 @@ impl Channel {
their_htlc_minimum_msat: 0,
our_htlc_minimum_msat: Channel::derive_our_htlc_minimum_msat(feerate),
their_to_self_delay: 0,
our_to_self_delay: config.own_channel_config.our_to_self_delay,
their_max_accepted_htlcs: 0,
minimum_depth: 0, // Filled in in accept_channel

Expand Down Expand Up @@ -526,6 +522,10 @@ impl Channel {
let chan_keys = keys_provider.get_channel_keys(true);
let mut local_config = (*config).channel_options.clone();

if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
return Err(ChannelError::Close("Configured with an unreasonable our_to_self_delay putting user funds at risks"));
}

// Check sanity of message fields:
if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS {
return Err(ChannelError::Close("funding value > 2^24"));
Expand All @@ -547,7 +547,7 @@ impl Channel {
}
Channel::check_remote_fee(fee_estimator, msg.feerate_per_kw)?;

if msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT {
if msg.to_self_delay > config.peer_channel_config_limits.their_to_self_delay || msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT {
return Err(ChannelError::Close("They wanted our payments to be delayed by a needlessly long period"));
}
if msg.max_accepted_htlcs < 1 {
Expand Down Expand Up @@ -620,7 +620,7 @@ impl Channel {

let secp_ctx = Secp256k1::new();
let mut channel_monitor = ChannelMonitor::new(&chan_keys.revocation_base_key, &chan_keys.delayed_payment_base_key,
&chan_keys.htlc_base_key, &chan_keys.payment_base_key, &keys_provider.get_shutdown_pubkey(), BREAKDOWN_TIMEOUT,
&chan_keys.htlc_base_key, &chan_keys.payment_base_key, &keys_provider.get_shutdown_pubkey(), config.own_channel_config.our_to_self_delay,
keys_provider.get_destination_script(), logger.clone());
channel_monitor.set_their_base_keys(&msg.htlc_basepoint, &msg.delayed_payment_basepoint);
channel_monitor.set_their_to_self_delay(msg.to_self_delay);
Expand Down Expand Up @@ -700,6 +700,7 @@ impl Channel {
their_htlc_minimum_msat: msg.htlc_minimum_msat,
our_htlc_minimum_msat: Channel::derive_our_htlc_minimum_msat(msg.feerate_per_kw as u64),
their_to_self_delay: msg.to_self_delay,
our_to_self_delay: config.own_channel_config.our_to_self_delay,
their_max_accepted_htlcs: msg.max_accepted_htlcs,
minimum_depth: config.own_channel_config.minimum_depth,

Expand Down Expand Up @@ -935,7 +936,7 @@ impl Channel {
log_trace!(self, " ...including {} output with value {}", if local { "to_local" } else { "to_remote" }, value_to_a);
txouts.push((TxOut {
script_pubkey: chan_utils::get_revokeable_redeemscript(&keys.revocation_key,
if local { self.their_to_self_delay } else { BREAKDOWN_TIMEOUT },
if local { self.their_to_self_delay } else { self.our_to_self_delay },
&keys.a_delayed_payment_key).to_v0_p2wsh(),
value: value_to_a as u64
}, None));
Expand Down Expand Up @@ -1134,7 +1135,7 @@ impl Channel {
/// @local is used only to convert relevant internal structures which refer to remote vs local
/// to decide value of outputs and direction of HTLCs.
fn build_htlc_transaction(&self, prev_hash: &Sha256dHash, htlc: &HTLCOutputInCommitment, local: bool, keys: &TxCreationKeys, feerate_per_kw: u64) -> Transaction {
chan_utils::build_htlc_transaction(prev_hash, feerate_per_kw, if local { self.their_to_self_delay } else { BREAKDOWN_TIMEOUT }, htlc, &keys.a_delayed_payment_key, &keys.revocation_key)
chan_utils::build_htlc_transaction(prev_hash, feerate_per_kw, if local { self.their_to_self_delay } else { self.our_to_self_delay }, htlc, &keys.a_delayed_payment_key, &keys.revocation_key)
}

fn create_htlc_tx_signature(&self, tx: &Transaction, htlc: &HTLCOutputInCommitment, keys: &TxCreationKeys) -> Result<(Script, Signature, bool), ChannelError> {
Expand Down Expand Up @@ -1388,7 +1389,7 @@ impl Channel {
if msg.htlc_minimum_msat >= (self.channel_value_satoshis - msg.channel_reserve_satoshis) * 1000 {
return Err(ChannelError::Close("Minimum htlc value is full channel value"));
}
if msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT {
if msg.to_self_delay > config.peer_channel_config_limits.their_to_self_delay || msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT {
return Err(ChannelError::Close("They wanted our payments to be delayed by a needlessly long period"));
}
if msg.max_accepted_htlcs < 1 {
Expand Down Expand Up @@ -3072,7 +3073,7 @@ impl Channel {
channel_reserve_satoshis: Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis),
htlc_minimum_msat: self.our_htlc_minimum_msat,
feerate_per_kw: fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background) as u32,
to_self_delay: BREAKDOWN_TIMEOUT,
to_self_delay: self.our_to_self_delay,
max_accepted_htlcs: OUR_MAX_HTLCS,
funding_pubkey: PublicKey::from_secret_key(&self.secp_ctx, &self.local_keys.funding_key),
revocation_basepoint: PublicKey::from_secret_key(&self.secp_ctx, &self.local_keys.revocation_base_key),
Expand Down Expand Up @@ -3105,7 +3106,7 @@ impl Channel {
channel_reserve_satoshis: Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis),
htlc_minimum_msat: self.our_htlc_minimum_msat,
minimum_depth: self.minimum_depth,
to_self_delay: BREAKDOWN_TIMEOUT,
to_self_delay: self.our_to_self_delay,
max_accepted_htlcs: OUR_MAX_HTLCS,
funding_pubkey: PublicKey::from_secret_key(&self.secp_ctx, &self.local_keys.funding_key),
revocation_basepoint: PublicKey::from_secret_key(&self.secp_ctx, &self.local_keys.revocation_base_key),
Expand Down Expand Up @@ -3754,6 +3755,7 @@ impl Writeable for Channel {
self.their_htlc_minimum_msat.write(writer)?;
self.our_htlc_minimum_msat.write(writer)?;
self.their_to_self_delay.write(writer)?;
self.our_to_self_delay.write(writer)?;
self.their_max_accepted_htlcs.write(writer)?;
self.minimum_depth.write(writer)?;

Expand Down Expand Up @@ -3915,6 +3917,7 @@ impl<R : ::std::io::Read> ReadableArgs<R, Arc<Logger>> for Channel {
let their_htlc_minimum_msat = Readable::read(reader)?;
let our_htlc_minimum_msat = Readable::read(reader)?;
let their_to_self_delay = Readable::read(reader)?;
let our_to_self_delay = Readable::read(reader)?;
let their_max_accepted_htlcs = Readable::read(reader)?;
let minimum_depth = Readable::read(reader)?;

Expand Down Expand Up @@ -3992,6 +3995,7 @@ impl<R : ::std::io::Read> ReadableArgs<R, Arc<Logger>> for Channel {
their_htlc_minimum_msat,
our_htlc_minimum_msat,
their_to_self_delay,
our_to_self_delay,
their_max_accepted_htlcs,
minimum_depth,

Expand Down
6 changes: 6 additions & 0 deletions src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,12 @@ pub struct ChannelManager {
logger: Arc<Logger>,
}

/// The amount of time we require our counterparty wait to claim their money (ie time between when
/// we, or our watchtower, must check for them having broadcast a theft transaction).
pub(crate) const BREAKDOWN_TIMEOUT: u16 = 6 * 24;
/// The amount of time we're willing to wait to claim money back to us
pub(crate) const MAX_LOCAL_BREAKDOWN_TIMEOUT: u16 = 6 * 24 * 7;

/// The minimum number of blocks between an inbound HTLC's CLTV and the corresponding outbound
/// HTLC's CLTV. This should always be a few blocks greater than channelmonitor::CLTV_CLAIM_BUFFER,
/// ie the node we forwarded the payment on to should always have enough room to reliably time out
Expand Down
2 changes: 1 addition & 1 deletion src/ln/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1768,7 +1768,7 @@ impl ChannelMonitor {
None => return (None, None),
Some(their_delayed_payment_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &their_delayed_payment_base_key)),
};
let redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.their_to_self_delay.unwrap(), &delayed_key);
let redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.our_to_self_delay, &delayed_key);
let revokeable_p2wsh = redeemscript.to_v0_p2wsh();
let htlc_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!

Expand Down
76 changes: 71 additions & 5 deletions src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

use chain::transaction::OutPoint;
use chain::chaininterface::{ChainListener, ChainWatchInterface};
use chain::keysinterface::{KeysInterface, SpendableOutputDescriptor};
use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC, BREAKDOWN_TIMEOUT};
use ln::channelmanager::{ChannelManager,ChannelManagerReadArgs,HTLCForwardInfo,RAACommitmentOrder, PaymentPreimage, PaymentHash};
use chain::keysinterface::{KeysInterface, SpendableOutputDescriptor, KeysManager};
use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC};
use ln::channelmanager::{ChannelManager,ChannelManagerReadArgs,HTLCForwardInfo,RAACommitmentOrder, PaymentPreimage, PaymentHash, BREAKDOWN_TIMEOUT};
use ln::channelmonitor::{ChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ManyChannelMonitor, ANTI_REORG_DELAY};
use ln::channel::{ACCEPTED_HTLC_SCRIPT_WEIGHT, OFFERED_HTLC_SCRIPT_WEIGHT};
use ln::channel::{ACCEPTED_HTLC_SCRIPT_WEIGHT, OFFERED_HTLC_SCRIPT_WEIGHT, Channel, ChannelError};
use ln::onion_utils;
use ln::router::{Route, RouteHop};
use ln::msgs;
Expand Down Expand Up @@ -1727,7 +1727,15 @@ fn channel_monitor_network_test() {
fn test_justice_tx() {
// Test justice txn built on revoked HTLC-Success tx, against both sides

let nodes = create_network(2, &[None, None]);
let mut alice_config = UserConfig::new();
alice_config.channel_options.announced_channel = true;
alice_config.peer_channel_config_limits.force_announced_channel_preference = false;
alice_config.own_channel_config.our_to_self_delay = 6 * 24 * 5;
let mut bob_config = UserConfig::new();
bob_config.channel_options.announced_channel = true;
bob_config.peer_channel_config_limits.force_announced_channel_preference = false;
bob_config.own_channel_config.our_to_self_delay = 6 * 24 * 3;
let nodes = create_network(2, &[Some(alice_config), Some(bob_config)]);
// Create some new channels:
let chan_5 = create_announced_chan_between_nodes(&nodes, 0, 1, LocalFeatures::new(), LocalFeatures::new());

Expand Down Expand Up @@ -5879,3 +5887,61 @@ fn test_upfront_shutdown_script() {
_ => panic!("Unexpected event"),
}
}

#[test]
fn test_user_configurable_csv_delay() {
// We test our channel constructors yield errors when we pass them absurd csv delay

let mut low_our_to_self_config = UserConfig::new();
low_our_to_self_config.own_channel_config.our_to_self_delay = 6;
let mut high_their_to_self_config = UserConfig::new();
high_their_to_self_config.peer_channel_config_limits.their_to_self_delay = 100;
let nodes = create_network(2, &[Some(high_their_to_self_config.clone()), None]);

// We test config.our_to_self > BREAKDOWN_TIMEOUT is enforced in Channel::new_outbound()
let keys_manager: Arc<KeysInterface> = Arc::new(KeysManager::new(&nodes[0].node_seed, Network::Testnet, Arc::new(test_utils::TestLogger::new()), 10, 20));
if let Err(error) = Channel::new_outbound(&test_utils::TestFeeEstimator { sat_per_kw: 253 }, &keys_manager, nodes[1].node.get_our_node_id(), 1000000, 1000000, 0, Arc::new(test_utils::TestLogger::new()), &low_our_to_self_config) {
match error {
APIError::APIMisuseError { err } => { assert_eq!(err, "Configured with an unreasonable our_to_self_delay putting user funds at risks"); },
_ => panic!("Unexpected event"),
}
} else { assert!(false) }

// We test config.our_to_self > BREAKDOWN_TIMEOUT is enforced in Channel::new_from_req()
nodes[1].node.create_channel(nodes[0].node.get_our_node_id(), 1000000, 1000000, 42).unwrap();
let mut open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[0].node.get_our_node_id());
open_channel.to_self_delay = 200;
if let Err(error) = Channel::new_from_req(&test_utils::TestFeeEstimator { sat_per_kw: 253 }, &keys_manager, nodes[1].node.get_our_node_id(), LocalFeatures::new(), &open_channel, 0, Arc::new(test_utils::TestLogger::new()), &low_our_to_self_config) {
match error {
ChannelError::Close(err) => { assert_eq!(err, "Configured with an unreasonable our_to_self_delay putting user funds at risks"); },
_ => panic!("Unexpected event"),
}
} else { assert!(false); }

// We test msg.to_self_delay <= config.their_to_self_delay is enforced in Chanel::accept_channel()
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 1000000, 1000000, 42).unwrap();
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), LocalFeatures::new(), &get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id())).unwrap();
let mut accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
accept_channel.to_self_delay = 200;
if let Err(error) = nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), LocalFeatures::new(), &accept_channel) {
if let Some(error) = error.action {
match error {
ErrorAction::SendErrorMessage { msg } => {
assert_eq!(msg.data,"They wanted our payments to be delayed by a needlessly long period");
},
_ => { assert!(false); }
}
} else { assert!(false); }
} else { assert!(false); }

// We test msg.to_self_delay <= config.their_to_self_delay is enforced in Channel::new_from_req()
nodes[1].node.create_channel(nodes[0].node.get_our_node_id(), 1000000, 1000000, 42).unwrap();
let mut open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[0].node.get_our_node_id());
open_channel.to_self_delay = 200;
if let Err(error) = Channel::new_from_req(&test_utils::TestFeeEstimator { sat_per_kw: 253 }, &keys_manager, nodes[1].node.get_our_node_id(), LocalFeatures::new(), &open_channel, 0, Arc::new(test_utils::TestLogger::new()), &high_their_to_self_config) {
match error {
ChannelError::Close(err) => { assert_eq!(err, "They wanted our payments to be delayed by a needlessly long period"); },
_ => panic!("Unexpected event"),
}
} else { assert!(false); }
}
23 changes: 23 additions & 0 deletions src/util/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Various user-configurable channel limits and settings which ChannelManager
//! applies for you.

use ln::channelmanager::{BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT};

/// Top-level config which holds ChannelHandshakeLimits and ChannelConfig.
#[derive(Clone, Debug)]
pub struct UserConfig {
Expand Down Expand Up @@ -30,13 +32,26 @@ pub struct ChannelHandshakeConfig {
/// Applied only for inbound channels (see ChannelHandshakeLimits::max_minimum_depth for the
/// equivalent limit applied to outbound channels).
pub minimum_depth: u32,
/// Set to the amount of time we require our counterparty to wait to claim their money.
///
/// It's one of the main parameter of our security model. We (or one of our watchtowers) MUST
/// be online to check for peer having broadcast a revoked transaction to steal our funds
/// at least once every our_to_self_delay blocks.
/// Default is BREAKDOWN_TIMEOUT, we enforce it as a minimum at channel opening so you can
/// tweak config to ask for more security, not less.
///
/// Meanwhile, asking for a too high delay, we bother peer to freeze funds for nothing in
/// case of an honest unilateral channel close, which implicitly decrease the economic value of
/// our channel.
pub our_to_self_delay: u16,
}

impl ChannelHandshakeConfig {
/// Provides sane defaults for `ChannelHandshakeConfig`
pub fn new() -> ChannelHandshakeConfig {
ChannelHandshakeConfig {
minimum_depth: 6,
our_to_self_delay: BREAKDOWN_TIMEOUT,
}
}
}
Expand Down Expand Up @@ -88,6 +103,13 @@ pub struct ChannelHandshakeLimits {
/// Defaults to true to make the default that no announced channels are possible (which is
/// appropriate for any nodes which are not online very reliably).
pub force_announced_channel_preference: bool,
/// Set to the amount of time we're willing to wait to claim money back to us.
///
/// Not checking this value would be a security issue, as our peer would be able to set it to
/// max relative lock-time (a year) and we would "lose" money as it would be locked for a long time.
/// Default is MAX_LOCAL_BREAKDOWN_TIMEOUT, which we also enforce as a maximum value
/// so you can tweak config to reduce the loss of having useless locked funds (if your peer accepts)
pub their_to_self_delay: u16
}

impl ChannelHandshakeLimits {
Expand All @@ -107,6 +129,7 @@ impl ChannelHandshakeLimits {
max_dust_limit_satoshis: <u64>::max_value(),
max_minimum_depth: 144,
force_announced_channel_preference: true,
their_to_self_delay: MAX_LOCAL_BREAKDOWN_TIMEOUT,
}
}
}
Expand Down