Skip to content
Closed
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
3 changes: 2 additions & 1 deletion fuzz/fuzz_targets/router_target.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -187,7 +187,7 @@ pub fn do_test(data: &[u8]) {
},
1 => {
let short_channel_id = slice_to_be64(get_slice!(8));
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id});
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id, is_permanent: false});
},
_ => return,
}
Expand DownExpand Up@@ -223,6 +223,7 @@ pub fn do_test(data: &[u8]) {
fee_proportional_millionths: slice_to_be32(get_slice!(4)),
cltv_expiry_delta: slice_to_be16(get_slice!(2)),
htlc_minimum_msat: slice_to_be64(get_slice!(8)),
last_update: 0,
});
}
&last_hops_vec[..]
Expand Down
11 changes: 8 additions & 3 deletions src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1545,7 +1545,7 @@ impl Channel {
Ok(())
}

/// Removes an outbound HTLC which has been commitment_signed by the remote end
/// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
#[inline]
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
for htlc in self.pending_outbound_htlcs.iter_mut() {
Expand All@@ -1559,13 +1559,13 @@ impl Channel {
};
match htlc.state {
OutboundHTLCState::LocalAnnounced(_) =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")),
OutboundHTLCState::Committed => {
htlc.state = OutboundHTLCState::RemoteRemoved;
htlc.fail_reason = fail_reason;
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")),
}
return Ok(&htlc.source);
}
Expand DownExpand Up@@ -2450,6 +2450,11 @@ impl Channel {
self.our_htlc_minimum_msat
}

/// Allowed in any state (including after shutdown)
pub fn get_their_htlc_minimum_msat(&self) -> u64 {
self.our_htlc_minimum_msat
}

pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
Expand Down
733 changes: 618 additions & 115 deletions src/ln/channelmanager.rs

Large diffs are not rendered by default.

46 changes: 31 additions & 15 deletions src/ln/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,13 @@ impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
const CLTV_SHARED_CLAIM_BUFFER: u32 = 12;
/// If an HTLC expires within this many blocks, force-close the channel to broadcast the
/// HTLC-Success transaction.
const CLTV_CLAIM_BUFFER: u32 = 6;
/// In other words, this is an upper bound on how many blocks we think it can take us to get a
/// transaction confirmed (and we use it in a few more, equivalent, places).
pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers).
pub(crate) const HTLC_FAIL_TIMEOUT_BLOCKS: u32 = 3;

#[derive(Clone, PartialEq)]
enum KeyStorage {
Expand DownExpand Up@@ -1184,16 +1190,7 @@ impl ChannelMonitor {
}
}
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
let mut needs_broadcast = false;
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
needs_broadcast = true;
}
}
}

if needs_broadcast {
if self.would_broadcast_at_height(height) {
broadcaster.broadcast_transaction(&cur_local_tx.tx);
for tx in self.broadcast_by_local_state(&cur_local_tx) {
broadcaster.broadcast_transaction(&tx);
Expand All@@ -1206,10 +1203,29 @@ impl ChannelMonitor {
pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool {
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
return true;
}
// For inbound HTLCs which we know the preimage for, we have to ensure we hit the
// chain with enough room to claim the HTLC without our counterparty being able to
// time out the HTLC first.
// For outbound HTLCs which our counterparty hasn't failed/claimed, our primary
// concern is being able to claim the corresponding inbound HTLC (on another
// channel) before it expires. In fact, we don't even really care if our
// counterparty here claims such an outbound HTLC after it expired as long as we
// can still claim the corresponding HTLC. Thus, to avoid needlessly hitting the
// chain when our counterparty is waiting for expiration to off-chain fail an HTLC
// we give ourselves a few blocks of headroom after expiration before going
// on-chain for an expired HTLC.
// Note that, to avoid a potential attack whereby a node delays claiming an HTLC
// from us until we've reached the point where we go on-chain with the
// corresponding inbound HTLC, we must ensure that outbound HTLCs go on chain at
// least CLTV_CLAIM_BUFFER blocks prior to the inbound HTLC.
// aka outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS == height - CLTV_CLAIM_BUFFER
// inbound_cltv == height + CLTV_CLAIM_BUFFER
// outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS + CLTV_CLAIM_BUFER <= inbound_cltv - CLTV_CLAIM_BUFFER
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= inbound_cltv - outbound_cltv
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= CLTV_EXPIRY_DELTA
if ( htlc.offered && htlc.cltv_expiry + HTLC_FAIL_TIMEOUT_BLOCKS <= height) ||
(!htlc.offered && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) {
return true;
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/ln/msgs.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -485,7 +485,19 @@ pub enum HTLCFailChannelUpdate {
ChannelClosed {
/// The short_channel_id which has now closed.
short_channel_id: u64,
/// when this true, this channel should be permanently removed from the
/// consideration. Otherwise, this channel can be restored as new channel_update is received
is_permanent: bool,
},
/// We received an error which indicated only that a node has failed
NodeFailure {
/// The node_id that has failed.
node_id: PublicKey,
/// when this true, node should be permanently removed from the
/// consideration. Otherwise, the channels connected to this node can be
/// restored as new channel_update is received
is_permanent: bool,
}
}

/// A trait to describe an object which can receive channel messages.
Expand DownExpand Up@@ -517,7 +529,7 @@ pub trait ChannelMessageHandler : events::EventsProvider + Send + Sync {
/// Handle an incoming update_fulfill_htlc message from the given peer.
fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_htlc message from the given peer.
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<Option<HTLCFailChannelUpdate>, HandleError>;
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_malformed_htlc message from the given peer.
fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC) -> Result<(), HandleError>;
/// Handle an incoming commitment_signed message from the given peer.
Expand Down
9 changes: 5 additions & 4 deletions src/ln/peer_handler.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -615,10 +615,7 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
},
131 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailHTLC::read(&mut reader));
let chan_update = try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
if let Some(update) = chan_update {
self.message_handler.route_handler.handle_htlc_fail_channel_update(&update);
}
try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
},
135 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailMalformedHTLC::read(&mut reader));
Expand DownExpand Up@@ -906,6 +903,10 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
}
continue;
},
Event::RouteUpdate { ref update } => {
self.message_handler.route_handler.handle_htlc_fail_channel_update(update);
continue;
},
Event::HandleError { ref node_id, ref action } => {
if let Some(ref action) = *action {
match *action {
Expand Down
20 changes: 19 additions & 1 deletion src/ln/router.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,9 @@ pub struct RouteHop {
/// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value
/// expected at the destination, in excess of the current block height.
pub cltv_expiry_delta: u32,
/// channel update timestamp of the outbound channel of each hop when this route is
/// calculated
pub channel_update_timestamp: u32,
}

/// A route from us through the network to a destination
Expand DownExpand Up@@ -168,6 +171,8 @@ impl NetworkMap {
pub struct RouteHint {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
/// last update
pub last_update: u32,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The static msat-denominated fee which must be paid to use this channel
Expand DownExpand Up@@ -349,12 +354,17 @@ impl RoutingMessageHandler for Router {
&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } => {
let _ = self.handle_channel_update(msg);
},
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => {
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id, is_permanent:_ } => {
let mut network = self.network_map.write().unwrap();
if let Some(chan) = network.channels.remove(short_channel_id) {
Self::remove_channel_in_nodes(&mut network.nodes, &chan, *short_channel_id);
}
},
&msgs::HTLCFailChannelUpdate::NodeFailure { ref node_id, is_permanent:_ } => {
//let mut network = self.network_map.write().unwrap();
//TODO: check _blamed_upstream_node
self.mark_node_bad(node_id, false);
},
}
}

Expand DownExpand Up@@ -450,6 +460,7 @@ impl cmp::PartialOrd for RouteGraphNode {

struct DummyDirectionalChannelInfo {
src_node_id: PublicKey,
last_update: u32,
cltv_expiry_delta: u32,
htlc_minimum_msat: u64,
fee_base_msat: u32,
Expand DownExpand Up@@ -560,6 +571,7 @@ impl Router {

let dummy_directional_info = DummyDirectionalChannelInfo { // used for first_hops routes
src_node_id: network.our_node_id.clone(),
last_update: 0,
cltv_expiry_delta: 0,
htlc_minimum_msat: 0,
fee_base_msat: 0,
Expand All@@ -580,6 +592,7 @@ impl Router {
short_channel_id,
fee_msat: final_value_msat,
cltv_expiry_delta: final_cltv,
channel_update_timestamp: 0, //TODO: related to local channel_update_handling?
}],
});
}
Expand DownExpand Up@@ -613,6 +626,7 @@ impl Router {
short_channel_id: 0,
fee_msat: 0,
cltv_expiry_delta: 0,
channel_update_timestamp: $directional_info.last_update,
})
});
if $directional_info.src_node_id != network.our_node_id {
Expand All@@ -639,6 +653,7 @@ impl Router {
short_channel_id: $chan_id.clone(),
fee_msat: new_fee, // This field is ignored on the last-hop anyway
cltv_expiry_delta: $directional_info.cltv_expiry_delta as u32,
channel_update_timestamp: $directional_info.last_update,
}
}
}
Expand DownExpand Up@@ -1161,20 +1176,23 @@ mod tests {
let mut last_hops = vec!(RouteHint {
src_node_id: node4.clone(),
short_channel_id: 8,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (8 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node5.clone(),
short_channel_id: 9,
last_update: 0,
fee_base_msat: 1001,
fee_proportional_millionths: 0,
cltv_expiry_delta: (9 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node6.clone(),
short_channel_id: 10,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (10 << 8) | 1,
Expand Down
11 changes: 11 additions & 0 deletions src/util/events.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,6 +75,10 @@ pub enum Event {
PaymentFailed {
/// The hash which was given to ChannelManager::send_payment.
payment_hash: [u8; 32],
/// Inindicates if the payment can be retried.
retryable: bool,
/// Error code from Onion failure message
error_code: Option<u16>,
},
/// Used to indicate that ChannelManager::process_pending_htlc_forwards should be called at a
/// time in the future.
Expand DownExpand Up@@ -161,6 +165,13 @@ pub enum Event {
node_id: PublicKey,
/// The action which should be taken.
action: Option<msgs::ErrorAction>
},

/// Used to indicate a change should be made into network map
///
RouteUpdate {
/// The channel/node update which should be sent to router
update: msgs::HTLCFailChannelUpdate,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/util/test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -110,7 +110,7 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFulfillHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<Option<msgs::HTLCFailChannelUpdate>, HandleError> {
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailMalformedHTLC) -> Result<(), HandleError> {
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Fill out failure codes for onion packet by yuntai · Pull Request #157 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
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
3 changes: 2 additions & 1 deletion fuzz/fuzz_targets/router_target.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -187,7 +187,7 @@ pub fn do_test(data: &[u8]) {
},
1 => {
let short_channel_id = slice_to_be64(get_slice!(8));
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id});
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id, is_permanent: false});
},
_ => return,
}
Expand DownExpand Up@@ -223,6 +223,7 @@ pub fn do_test(data: &[u8]) {
fee_proportional_millionths: slice_to_be32(get_slice!(4)),
cltv_expiry_delta: slice_to_be16(get_slice!(2)),
htlc_minimum_msat: slice_to_be64(get_slice!(8)),
last_update: 0,
});
}
&last_hops_vec[..]
Expand Down
11 changes: 8 additions & 3 deletions src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1545,7 +1545,7 @@ impl Channel {
Ok(())
}

/// Removes an outbound HTLC which has been commitment_signed by the remote end
/// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
#[inline]
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
for htlc in self.pending_outbound_htlcs.iter_mut() {
Expand All@@ -1559,13 +1559,13 @@ impl Channel {
};
match htlc.state {
OutboundHTLCState::LocalAnnounced(_) =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")),
OutboundHTLCState::Committed => {
htlc.state = OutboundHTLCState::RemoteRemoved;
htlc.fail_reason = fail_reason;
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")),
}
return Ok(&htlc.source);
}
Expand DownExpand Up@@ -2450,6 +2450,11 @@ impl Channel {
self.our_htlc_minimum_msat
}

/// Allowed in any state (including after shutdown)
pub fn get_their_htlc_minimum_msat(&self) -> u64 {
self.our_htlc_minimum_msat
}

pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
Expand Down
733 changes: 618 additions & 115 deletions src/ln/channelmanager.rs

Large diffs are not rendered by default.

46 changes: 31 additions & 15 deletions src/ln/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,13 @@ impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
const CLTV_SHARED_CLAIM_BUFFER: u32 = 12;
/// If an HTLC expires within this many blocks, force-close the channel to broadcast the
/// HTLC-Success transaction.
const CLTV_CLAIM_BUFFER: u32 = 6;
/// In other words, this is an upper bound on how many blocks we think it can take us to get a
/// transaction confirmed (and we use it in a few more, equivalent, places).
pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers).
pub(crate) const HTLC_FAIL_TIMEOUT_BLOCKS: u32 = 3;

#[derive(Clone, PartialEq)]
enum KeyStorage {
Expand DownExpand Up@@ -1184,16 +1190,7 @@ impl ChannelMonitor {
}
}
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
let mut needs_broadcast = false;
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
needs_broadcast = true;
}
}
}

if needs_broadcast {
if self.would_broadcast_at_height(height) {
broadcaster.broadcast_transaction(&cur_local_tx.tx);
for tx in self.broadcast_by_local_state(&cur_local_tx) {
broadcaster.broadcast_transaction(&tx);
Expand All@@ -1206,10 +1203,29 @@ impl ChannelMonitor {
pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool {
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
return true;
}
// For inbound HTLCs which we know the preimage for, we have to ensure we hit the
// chain with enough room to claim the HTLC without our counterparty being able to
// time out the HTLC first.
// For outbound HTLCs which our counterparty hasn't failed/claimed, our primary
// concern is being able to claim the corresponding inbound HTLC (on another
// channel) before it expires. In fact, we don't even really care if our
// counterparty here claims such an outbound HTLC after it expired as long as we
// can still claim the corresponding HTLC. Thus, to avoid needlessly hitting the
// chain when our counterparty is waiting for expiration to off-chain fail an HTLC
// we give ourselves a few blocks of headroom after expiration before going
// on-chain for an expired HTLC.
// Note that, to avoid a potential attack whereby a node delays claiming an HTLC
// from us until we've reached the point where we go on-chain with the
// corresponding inbound HTLC, we must ensure that outbound HTLCs go on chain at
// least CLTV_CLAIM_BUFFER blocks prior to the inbound HTLC.
// aka outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS == height - CLTV_CLAIM_BUFFER
// inbound_cltv == height + CLTV_CLAIM_BUFFER
// outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS + CLTV_CLAIM_BUFER <= inbound_cltv - CLTV_CLAIM_BUFFER
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= inbound_cltv - outbound_cltv
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= CLTV_EXPIRY_DELTA
if ( htlc.offered && htlc.cltv_expiry + HTLC_FAIL_TIMEOUT_BLOCKS <= height) ||
(!htlc.offered && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) {
return true;
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/ln/msgs.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -485,7 +485,19 @@ pub enum HTLCFailChannelUpdate {
ChannelClosed {
/// The short_channel_id which has now closed.
short_channel_id: u64,
/// when this true, this channel should be permanently removed from the
/// consideration. Otherwise, this channel can be restored as new channel_update is received
is_permanent: bool,
},
/// We received an error which indicated only that a node has failed
NodeFailure {
/// The node_id that has failed.
node_id: PublicKey,
/// when this true, node should be permanently removed from the
/// consideration. Otherwise, the channels connected to this node can be
/// restored as new channel_update is received
is_permanent: bool,
}
}

/// A trait to describe an object which can receive channel messages.
Expand DownExpand Up@@ -517,7 +529,7 @@ pub trait ChannelMessageHandler : events::EventsProvider + Send + Sync {
/// Handle an incoming update_fulfill_htlc message from the given peer.
fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_htlc message from the given peer.
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<Option<HTLCFailChannelUpdate>, HandleError>;
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_malformed_htlc message from the given peer.
fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC) -> Result<(), HandleError>;
/// Handle an incoming commitment_signed message from the given peer.
Expand Down
9 changes: 5 additions & 4 deletions src/ln/peer_handler.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -615,10 +615,7 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
},
131 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailHTLC::read(&mut reader));
let chan_update = try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
if let Some(update) = chan_update {
self.message_handler.route_handler.handle_htlc_fail_channel_update(&update);
}
try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
},
135 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailMalformedHTLC::read(&mut reader));
Expand DownExpand Up@@ -906,6 +903,10 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
}
continue;
},
Event::RouteUpdate { ref update } => {
self.message_handler.route_handler.handle_htlc_fail_channel_update(update);
continue;
},
Event::HandleError { ref node_id, ref action } => {
if let Some(ref action) = *action {
match *action {
Expand Down
20 changes: 19 additions & 1 deletion src/ln/router.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,9 @@ pub struct RouteHop {
/// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value
/// expected at the destination, in excess of the current block height.
pub cltv_expiry_delta: u32,
/// channel update timestamp of the outbound channel of each hop when this route is
/// calculated
pub channel_update_timestamp: u32,
}

/// A route from us through the network to a destination
Expand DownExpand Up@@ -168,6 +171,8 @@ impl NetworkMap {
pub struct RouteHint {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
/// last update
pub last_update: u32,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The static msat-denominated fee which must be paid to use this channel
Expand DownExpand Up@@ -349,12 +354,17 @@ impl RoutingMessageHandler for Router {
&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } => {
let _ = self.handle_channel_update(msg);
},
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => {
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id, is_permanent:_ } => {
let mut network = self.network_map.write().unwrap();
if let Some(chan) = network.channels.remove(short_channel_id) {
Self::remove_channel_in_nodes(&mut network.nodes, &chan, *short_channel_id);
}
},
&msgs::HTLCFailChannelUpdate::NodeFailure { ref node_id, is_permanent:_ } => {
//let mut network = self.network_map.write().unwrap();
//TODO: check _blamed_upstream_node
self.mark_node_bad(node_id, false);
},
}
}

Expand DownExpand Up@@ -450,6 +460,7 @@ impl cmp::PartialOrd for RouteGraphNode {

struct DummyDirectionalChannelInfo {
src_node_id: PublicKey,
last_update: u32,
cltv_expiry_delta: u32,
htlc_minimum_msat: u64,
fee_base_msat: u32,
Expand DownExpand Up@@ -560,6 +571,7 @@ impl Router {

let dummy_directional_info = DummyDirectionalChannelInfo { // used for first_hops routes
src_node_id: network.our_node_id.clone(),
last_update: 0,
cltv_expiry_delta: 0,
htlc_minimum_msat: 0,
fee_base_msat: 0,
Expand All@@ -580,6 +592,7 @@ impl Router {
short_channel_id,
fee_msat: final_value_msat,
cltv_expiry_delta: final_cltv,
channel_update_timestamp: 0, //TODO: related to local channel_update_handling?
}],
});
}
Expand DownExpand Up@@ -613,6 +626,7 @@ impl Router {
short_channel_id: 0,
fee_msat: 0,
cltv_expiry_delta: 0,
channel_update_timestamp: $directional_info.last_update,
})
});
if $directional_info.src_node_id != network.our_node_id {
Expand All@@ -639,6 +653,7 @@ impl Router {
short_channel_id: $chan_id.clone(),
fee_msat: new_fee, // This field is ignored on the last-hop anyway
cltv_expiry_delta: $directional_info.cltv_expiry_delta as u32,
channel_update_timestamp: $directional_info.last_update,
}
}
}
Expand DownExpand Up@@ -1161,20 +1176,23 @@ mod tests {
let mut last_hops = vec!(RouteHint {
src_node_id: node4.clone(),
short_channel_id: 8,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (8 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node5.clone(),
short_channel_id: 9,
last_update: 0,
fee_base_msat: 1001,
fee_proportional_millionths: 0,
cltv_expiry_delta: (9 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node6.clone(),
short_channel_id: 10,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (10 << 8) | 1,
Expand Down
11 changes: 11 additions & 0 deletions src/util/events.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,6 +75,10 @@ pub enum Event {
PaymentFailed {
/// The hash which was given to ChannelManager::send_payment.
payment_hash: [u8; 32],
/// Inindicates if the payment can be retried.
retryable: bool,
/// Error code from Onion failure message
error_code: Option<u16>,
},
/// Used to indicate that ChannelManager::process_pending_htlc_forwards should be called at a
/// time in the future.
Expand DownExpand Up@@ -161,6 +165,13 @@ pub enum Event {
node_id: PublicKey,
/// The action which should be taken.
action: Option<msgs::ErrorAction>
},

/// Used to indicate a change should be made into network map
///
RouteUpdate {
/// The channel/node update which should be sent to router
update: msgs::HTLCFailChannelUpdate,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/util/test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -110,7 +110,7 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFulfillHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<Option<msgs::HTLCFailChannelUpdate>, HandleError> {
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailMalformedHTLC) -> Result<(), HandleError> {
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fill out failure codes for onion packet by yuntai · Pull Request #157 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
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
3 changes: 2 additions & 1 deletion fuzz/fuzz_targets/router_target.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -187,7 +187,7 @@ pub fn do_test(data: &[u8]) {
},
1 => {
let short_channel_id = slice_to_be64(get_slice!(8));
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id});
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id, is_permanent: false});
},
_ => return,
}
Expand DownExpand Up@@ -223,6 +223,7 @@ pub fn do_test(data: &[u8]) {
fee_proportional_millionths: slice_to_be32(get_slice!(4)),
cltv_expiry_delta: slice_to_be16(get_slice!(2)),
htlc_minimum_msat: slice_to_be64(get_slice!(8)),
last_update: 0,
});
}
&last_hops_vec[..]
Expand Down
11 changes: 8 additions & 3 deletions src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1545,7 +1545,7 @@ impl Channel {
Ok(())
}

/// Removes an outbound HTLC which has been commitment_signed by the remote end
/// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
#[inline]
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
for htlc in self.pending_outbound_htlcs.iter_mut() {
Expand All@@ -1559,13 +1559,13 @@ impl Channel {
};
match htlc.state {
OutboundHTLCState::LocalAnnounced(_) =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")),
OutboundHTLCState::Committed => {
htlc.state = OutboundHTLCState::RemoteRemoved;
htlc.fail_reason = fail_reason;
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")),
}
return Ok(&htlc.source);
}
Expand DownExpand Up@@ -2450,6 +2450,11 @@ impl Channel {
self.our_htlc_minimum_msat
}

/// Allowed in any state (including after shutdown)
pub fn get_their_htlc_minimum_msat(&self) -> u64 {
self.our_htlc_minimum_msat
}

pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
Expand Down
733 changes: 618 additions & 115 deletions src/ln/channelmanager.rs

Large diffs are not rendered by default.

46 changes: 31 additions & 15 deletions src/ln/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,13 @@ impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
const CLTV_SHARED_CLAIM_BUFFER: u32 = 12;
/// If an HTLC expires within this many blocks, force-close the channel to broadcast the
/// HTLC-Success transaction.
const CLTV_CLAIM_BUFFER: u32 = 6;
/// In other words, this is an upper bound on how many blocks we think it can take us to get a
/// transaction confirmed (and we use it in a few more, equivalent, places).
pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers).
pub(crate) const HTLC_FAIL_TIMEOUT_BLOCKS: u32 = 3;

#[derive(Clone, PartialEq)]
enum KeyStorage {
Expand DownExpand Up@@ -1184,16 +1190,7 @@ impl ChannelMonitor {
}
}
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
let mut needs_broadcast = false;
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
needs_broadcast = true;
}
}
}

if needs_broadcast {
if self.would_broadcast_at_height(height) {
broadcaster.broadcast_transaction(&cur_local_tx.tx);
for tx in self.broadcast_by_local_state(&cur_local_tx) {
broadcaster.broadcast_transaction(&tx);
Expand All@@ -1206,10 +1203,29 @@ impl ChannelMonitor {
pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool {
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
return true;
}
// For inbound HTLCs which we know the preimage for, we have to ensure we hit the
// chain with enough room to claim the HTLC without our counterparty being able to
// time out the HTLC first.
// For outbound HTLCs which our counterparty hasn't failed/claimed, our primary
// concern is being able to claim the corresponding inbound HTLC (on another
// channel) before it expires. In fact, we don't even really care if our
// counterparty here claims such an outbound HTLC after it expired as long as we
// can still claim the corresponding HTLC. Thus, to avoid needlessly hitting the
// chain when our counterparty is waiting for expiration to off-chain fail an HTLC
// we give ourselves a few blocks of headroom after expiration before going
// on-chain for an expired HTLC.
// Note that, to avoid a potential attack whereby a node delays claiming an HTLC
// from us until we've reached the point where we go on-chain with the
// corresponding inbound HTLC, we must ensure that outbound HTLCs go on chain at
// least CLTV_CLAIM_BUFFER blocks prior to the inbound HTLC.
// aka outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS == height - CLTV_CLAIM_BUFFER
// inbound_cltv == height + CLTV_CLAIM_BUFFER
// outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS + CLTV_CLAIM_BUFER <= inbound_cltv - CLTV_CLAIM_BUFFER
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= inbound_cltv - outbound_cltv
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= CLTV_EXPIRY_DELTA
if ( htlc.offered && htlc.cltv_expiry + HTLC_FAIL_TIMEOUT_BLOCKS <= height) ||
(!htlc.offered && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) {
return true;
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/ln/msgs.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -485,7 +485,19 @@ pub enum HTLCFailChannelUpdate {
ChannelClosed {
/// The short_channel_id which has now closed.
short_channel_id: u64,
/// when this true, this channel should be permanently removed from the
/// consideration. Otherwise, this channel can be restored as new channel_update is received
is_permanent: bool,
},
/// We received an error which indicated only that a node has failed
NodeFailure {
/// The node_id that has failed.
node_id: PublicKey,
/// when this true, node should be permanently removed from the
/// consideration. Otherwise, the channels connected to this node can be
/// restored as new channel_update is received
is_permanent: bool,
}
}

/// A trait to describe an object which can receive channel messages.
Expand DownExpand Up@@ -517,7 +529,7 @@ pub trait ChannelMessageHandler : events::EventsProvider + Send + Sync {
/// Handle an incoming update_fulfill_htlc message from the given peer.
fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_htlc message from the given peer.
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<Option<HTLCFailChannelUpdate>, HandleError>;
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_malformed_htlc message from the given peer.
fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC) -> Result<(), HandleError>;
/// Handle an incoming commitment_signed message from the given peer.
Expand Down
9 changes: 5 additions & 4 deletions src/ln/peer_handler.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -615,10 +615,7 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
},
131 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailHTLC::read(&mut reader));
let chan_update = try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
if let Some(update) = chan_update {
self.message_handler.route_handler.handle_htlc_fail_channel_update(&update);
}
try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
},
135 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailMalformedHTLC::read(&mut reader));
Expand DownExpand Up@@ -906,6 +903,10 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
}
continue;
},
Event::RouteUpdate { ref update } => {
self.message_handler.route_handler.handle_htlc_fail_channel_update(update);
continue;
},
Event::HandleError { ref node_id, ref action } => {
if let Some(ref action) = *action {
match *action {
Expand Down
20 changes: 19 additions & 1 deletion src/ln/router.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,9 @@ pub struct RouteHop {
/// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value
/// expected at the destination, in excess of the current block height.
pub cltv_expiry_delta: u32,
/// channel update timestamp of the outbound channel of each hop when this route is
/// calculated
pub channel_update_timestamp: u32,
}

/// A route from us through the network to a destination
Expand DownExpand Up@@ -168,6 +171,8 @@ impl NetworkMap {
pub struct RouteHint {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
/// last update
pub last_update: u32,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The static msat-denominated fee which must be paid to use this channel
Expand DownExpand Up@@ -349,12 +354,17 @@ impl RoutingMessageHandler for Router {
&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } => {
let _ = self.handle_channel_update(msg);
},
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => {
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id, is_permanent:_ } => {
let mut network = self.network_map.write().unwrap();
if let Some(chan) = network.channels.remove(short_channel_id) {
Self::remove_channel_in_nodes(&mut network.nodes, &chan, *short_channel_id);
}
},
&msgs::HTLCFailChannelUpdate::NodeFailure { ref node_id, is_permanent:_ } => {
//let mut network = self.network_map.write().unwrap();
//TODO: check _blamed_upstream_node
self.mark_node_bad(node_id, false);
},
}
}

Expand DownExpand Up@@ -450,6 +460,7 @@ impl cmp::PartialOrd for RouteGraphNode {

struct DummyDirectionalChannelInfo {
src_node_id: PublicKey,
last_update: u32,
cltv_expiry_delta: u32,
htlc_minimum_msat: u64,
fee_base_msat: u32,
Expand DownExpand Up@@ -560,6 +571,7 @@ impl Router {

let dummy_directional_info = DummyDirectionalChannelInfo { // used for first_hops routes
src_node_id: network.our_node_id.clone(),
last_update: 0,
cltv_expiry_delta: 0,
htlc_minimum_msat: 0,
fee_base_msat: 0,
Expand All@@ -580,6 +592,7 @@ impl Router {
short_channel_id,
fee_msat: final_value_msat,
cltv_expiry_delta: final_cltv,
channel_update_timestamp: 0, //TODO: related to local channel_update_handling?
}],
});
}
Expand DownExpand Up@@ -613,6 +626,7 @@ impl Router {
short_channel_id: 0,
fee_msat: 0,
cltv_expiry_delta: 0,
channel_update_timestamp: $directional_info.last_update,
})
});
if $directional_info.src_node_id != network.our_node_id {
Expand All@@ -639,6 +653,7 @@ impl Router {
short_channel_id: $chan_id.clone(),
fee_msat: new_fee, // This field is ignored on the last-hop anyway
cltv_expiry_delta: $directional_info.cltv_expiry_delta as u32,
channel_update_timestamp: $directional_info.last_update,
}
}
}
Expand DownExpand Up@@ -1161,20 +1176,23 @@ mod tests {
let mut last_hops = vec!(RouteHint {
src_node_id: node4.clone(),
short_channel_id: 8,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (8 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node5.clone(),
short_channel_id: 9,
last_update: 0,
fee_base_msat: 1001,
fee_proportional_millionths: 0,
cltv_expiry_delta: (9 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node6.clone(),
short_channel_id: 10,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (10 << 8) | 1,
Expand Down
11 changes: 11 additions & 0 deletions src/util/events.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,6 +75,10 @@ pub enum Event {
PaymentFailed {
/// The hash which was given to ChannelManager::send_payment.
payment_hash: [u8; 32],
/// Inindicates if the payment can be retried.
retryable: bool,
/// Error code from Onion failure message
error_code: Option<u16>,
},
/// Used to indicate that ChannelManager::process_pending_htlc_forwards should be called at a
/// time in the future.
Expand DownExpand Up@@ -161,6 +165,13 @@ pub enum Event {
node_id: PublicKey,
/// The action which should be taken.
action: Option<msgs::ErrorAction>
},

/// Used to indicate a change should be made into network map
///
RouteUpdate {
/// The channel/node update which should be sent to router
update: msgs::HTLCFailChannelUpdate,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/util/test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -110,7 +110,7 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFulfillHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<Option<msgs::HTLCFailChannelUpdate>, HandleError> {
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailMalformedHTLC) -> Result<(), HandleError> {
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fill out failure codes for onion packet by yuntai · Pull Request #157 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
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
3 changes: 2 additions & 1 deletion fuzz/fuzz_targets/router_target.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -187,7 +187,7 @@ pub fn do_test(data: &[u8]) {
},
1 => {
let short_channel_id = slice_to_be64(get_slice!(8));
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id});
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id, is_permanent: false});
},
_ => return,
}
Expand DownExpand Up@@ -223,6 +223,7 @@ pub fn do_test(data: &[u8]) {
fee_proportional_millionths: slice_to_be32(get_slice!(4)),
cltv_expiry_delta: slice_to_be16(get_slice!(2)),
htlc_minimum_msat: slice_to_be64(get_slice!(8)),
last_update: 0,
});
}
&last_hops_vec[..]
Expand Down
11 changes: 8 additions & 3 deletions src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1545,7 +1545,7 @@ impl Channel {
Ok(())
}

/// Removes an outbound HTLC which has been commitment_signed by the remote end
/// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
#[inline]
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
for htlc in self.pending_outbound_htlcs.iter_mut() {
Expand All@@ -1559,13 +1559,13 @@ impl Channel {
};
match htlc.state {
OutboundHTLCState::LocalAnnounced(_) =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")),
OutboundHTLCState::Committed => {
htlc.state = OutboundHTLCState::RemoteRemoved;
htlc.fail_reason = fail_reason;
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")),
}
return Ok(&htlc.source);
}
Expand DownExpand Up@@ -2450,6 +2450,11 @@ impl Channel {
self.our_htlc_minimum_msat
}

/// Allowed in any state (including after shutdown)
pub fn get_their_htlc_minimum_msat(&self) -> u64 {
self.our_htlc_minimum_msat
}

pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
Expand Down
733 changes: 618 additions & 115 deletions src/ln/channelmanager.rs

Large diffs are not rendered by default.

46 changes: 31 additions & 15 deletions src/ln/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,13 @@ impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
const CLTV_SHARED_CLAIM_BUFFER: u32 = 12;
/// If an HTLC expires within this many blocks, force-close the channel to broadcast the
/// HTLC-Success transaction.
const CLTV_CLAIM_BUFFER: u32 = 6;
/// In other words, this is an upper bound on how many blocks we think it can take us to get a
/// transaction confirmed (and we use it in a few more, equivalent, places).
pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers).
pub(crate) const HTLC_FAIL_TIMEOUT_BLOCKS: u32 = 3;

#[derive(Clone, PartialEq)]
enum KeyStorage {
Expand DownExpand Up@@ -1184,16 +1190,7 @@ impl ChannelMonitor {
}
}
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
let mut needs_broadcast = false;
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
needs_broadcast = true;
}
}
}

if needs_broadcast {
if self.would_broadcast_at_height(height) {
broadcaster.broadcast_transaction(&cur_local_tx.tx);
for tx in self.broadcast_by_local_state(&cur_local_tx) {
broadcaster.broadcast_transaction(&tx);
Expand All@@ -1206,10 +1203,29 @@ impl ChannelMonitor {
pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool {
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
return true;
}
// For inbound HTLCs which we know the preimage for, we have to ensure we hit the
// chain with enough room to claim the HTLC without our counterparty being able to
// time out the HTLC first.
// For outbound HTLCs which our counterparty hasn't failed/claimed, our primary
// concern is being able to claim the corresponding inbound HTLC (on another
// channel) before it expires. In fact, we don't even really care if our
// counterparty here claims such an outbound HTLC after it expired as long as we
// can still claim the corresponding HTLC. Thus, to avoid needlessly hitting the
// chain when our counterparty is waiting for expiration to off-chain fail an HTLC
// we give ourselves a few blocks of headroom after expiration before going
// on-chain for an expired HTLC.
// Note that, to avoid a potential attack whereby a node delays claiming an HTLC
// from us until we've reached the point where we go on-chain with the
// corresponding inbound HTLC, we must ensure that outbound HTLCs go on chain at
// least CLTV_CLAIM_BUFFER blocks prior to the inbound HTLC.
// aka outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS == height - CLTV_CLAIM_BUFFER
// inbound_cltv == height + CLTV_CLAIM_BUFFER
// outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS + CLTV_CLAIM_BUFER <= inbound_cltv - CLTV_CLAIM_BUFFER
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= inbound_cltv - outbound_cltv
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= CLTV_EXPIRY_DELTA
if ( htlc.offered && htlc.cltv_expiry + HTLC_FAIL_TIMEOUT_BLOCKS <= height) ||
(!htlc.offered && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) {
return true;
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/ln/msgs.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -485,7 +485,19 @@ pub enum HTLCFailChannelUpdate {
ChannelClosed {
/// The short_channel_id which has now closed.
short_channel_id: u64,
/// when this true, this channel should be permanently removed from the
/// consideration. Otherwise, this channel can be restored as new channel_update is received
is_permanent: bool,
},
/// We received an error which indicated only that a node has failed
NodeFailure {
/// The node_id that has failed.
node_id: PublicKey,
/// when this true, node should be permanently removed from the
/// consideration. Otherwise, the channels connected to this node can be
/// restored as new channel_update is received
is_permanent: bool,
}
}

/// A trait to describe an object which can receive channel messages.
Expand DownExpand Up@@ -517,7 +529,7 @@ pub trait ChannelMessageHandler : events::EventsProvider + Send + Sync {
/// Handle an incoming update_fulfill_htlc message from the given peer.
fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_htlc message from the given peer.
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<Option<HTLCFailChannelUpdate>, HandleError>;
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_malformed_htlc message from the given peer.
fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC) -> Result<(), HandleError>;
/// Handle an incoming commitment_signed message from the given peer.
Expand Down
9 changes: 5 additions & 4 deletions src/ln/peer_handler.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -615,10 +615,7 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
},
131 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailHTLC::read(&mut reader));
let chan_update = try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
if let Some(update) = chan_update {
self.message_handler.route_handler.handle_htlc_fail_channel_update(&update);
}
try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
},
135 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailMalformedHTLC::read(&mut reader));
Expand DownExpand Up@@ -906,6 +903,10 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
}
continue;
},
Event::RouteUpdate { ref update } => {
self.message_handler.route_handler.handle_htlc_fail_channel_update(update);
continue;
},
Event::HandleError { ref node_id, ref action } => {
if let Some(ref action) = *action {
match *action {
Expand Down
20 changes: 19 additions & 1 deletion src/ln/router.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,9 @@ pub struct RouteHop {
/// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value
/// expected at the destination, in excess of the current block height.
pub cltv_expiry_delta: u32,
/// channel update timestamp of the outbound channel of each hop when this route is
/// calculated
pub channel_update_timestamp: u32,
}

/// A route from us through the network to a destination
Expand DownExpand Up@@ -168,6 +171,8 @@ impl NetworkMap {
pub struct RouteHint {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
/// last update
pub last_update: u32,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The static msat-denominated fee which must be paid to use this channel
Expand DownExpand Up@@ -349,12 +354,17 @@ impl RoutingMessageHandler for Router {
&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } => {
let _ = self.handle_channel_update(msg);
},
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => {
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id, is_permanent:_ } => {
let mut network = self.network_map.write().unwrap();
if let Some(chan) = network.channels.remove(short_channel_id) {
Self::remove_channel_in_nodes(&mut network.nodes, &chan, *short_channel_id);
}
},
&msgs::HTLCFailChannelUpdate::NodeFailure { ref node_id, is_permanent:_ } => {
//let mut network = self.network_map.write().unwrap();
//TODO: check _blamed_upstream_node
self.mark_node_bad(node_id, false);
},
}
}

Expand DownExpand Up@@ -450,6 +460,7 @@ impl cmp::PartialOrd for RouteGraphNode {

struct DummyDirectionalChannelInfo {
src_node_id: PublicKey,
last_update: u32,
cltv_expiry_delta: u32,
htlc_minimum_msat: u64,
fee_base_msat: u32,
Expand DownExpand Up@@ -560,6 +571,7 @@ impl Router {

let dummy_directional_info = DummyDirectionalChannelInfo { // used for first_hops routes
src_node_id: network.our_node_id.clone(),
last_update: 0,
cltv_expiry_delta: 0,
htlc_minimum_msat: 0,
fee_base_msat: 0,
Expand All@@ -580,6 +592,7 @@ impl Router {
short_channel_id,
fee_msat: final_value_msat,
cltv_expiry_delta: final_cltv,
channel_update_timestamp: 0, //TODO: related to local channel_update_handling?
}],
});
}
Expand DownExpand Up@@ -613,6 +626,7 @@ impl Router {
short_channel_id: 0,
fee_msat: 0,
cltv_expiry_delta: 0,
channel_update_timestamp: $directional_info.last_update,
})
});
if $directional_info.src_node_id != network.our_node_id {
Expand All@@ -639,6 +653,7 @@ impl Router {
short_channel_id: $chan_id.clone(),
fee_msat: new_fee, // This field is ignored on the last-hop anyway
cltv_expiry_delta: $directional_info.cltv_expiry_delta as u32,
channel_update_timestamp: $directional_info.last_update,
}
}
}
Expand DownExpand Up@@ -1161,20 +1176,23 @@ mod tests {
let mut last_hops = vec!(RouteHint {
src_node_id: node4.clone(),
short_channel_id: 8,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (8 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node5.clone(),
short_channel_id: 9,
last_update: 0,
fee_base_msat: 1001,
fee_proportional_millionths: 0,
cltv_expiry_delta: (9 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node6.clone(),
short_channel_id: 10,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (10 << 8) | 1,
Expand Down
11 changes: 11 additions & 0 deletions src/util/events.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,6 +75,10 @@ pub enum Event {
PaymentFailed {
/// The hash which was given to ChannelManager::send_payment.
payment_hash: [u8; 32],
/// Inindicates if the payment can be retried.
retryable: bool,
/// Error code from Onion failure message
error_code: Option<u16>,
},
/// Used to indicate that ChannelManager::process_pending_htlc_forwards should be called at a
/// time in the future.
Expand DownExpand Up@@ -161,6 +165,13 @@ pub enum Event {
node_id: PublicKey,
/// The action which should be taken.
action: Option<msgs::ErrorAction>
},

/// Used to indicate a change should be made into network map
///
RouteUpdate {
/// The channel/node update which should be sent to router
update: msgs::HTLCFailChannelUpdate,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/util/test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -110,7 +110,7 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFulfillHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<Option<msgs::HTLCFailChannelUpdate>, HandleError> {
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailMalformedHTLC) -> Result<(), HandleError> {
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Fill out failure codes for onion packet by yuntai · Pull Request #157 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
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
3 changes: 2 additions & 1 deletion fuzz/fuzz_targets/router_target.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -187,7 +187,7 @@ pub fn do_test(data: &[u8]) {
},
1 => {
let short_channel_id = slice_to_be64(get_slice!(8));
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id});
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id, is_permanent: false});
},
_ => return,
}
Expand DownExpand Up@@ -223,6 +223,7 @@ pub fn do_test(data: &[u8]) {
fee_proportional_millionths: slice_to_be32(get_slice!(4)),
cltv_expiry_delta: slice_to_be16(get_slice!(2)),
htlc_minimum_msat: slice_to_be64(get_slice!(8)),
last_update: 0,
});
}
&last_hops_vec[..]
Expand Down
11 changes: 8 additions & 3 deletions src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1545,7 +1545,7 @@ impl Channel {
Ok(())
}

/// Removes an outbound HTLC which has been commitment_signed by the remote end
/// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
#[inline]
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
for htlc in self.pending_outbound_htlcs.iter_mut() {
Expand All@@ -1559,13 +1559,13 @@ impl Channel {
};
match htlc.state {
OutboundHTLCState::LocalAnnounced(_) =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")),
OutboundHTLCState::Committed => {
htlc.state = OutboundHTLCState::RemoteRemoved;
htlc.fail_reason = fail_reason;
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")),
}
return Ok(&htlc.source);
}
Expand DownExpand Up@@ -2450,6 +2450,11 @@ impl Channel {
self.our_htlc_minimum_msat
}

/// Allowed in any state (including after shutdown)
pub fn get_their_htlc_minimum_msat(&self) -> u64 {
self.our_htlc_minimum_msat
}

pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
Expand Down
733 changes: 618 additions & 115 deletions src/ln/channelmanager.rs

Large diffs are not rendered by default.

46 changes: 31 additions & 15 deletions src/ln/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,13 @@ impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
const CLTV_SHARED_CLAIM_BUFFER: u32 = 12;
/// If an HTLC expires within this many blocks, force-close the channel to broadcast the
/// HTLC-Success transaction.
const CLTV_CLAIM_BUFFER: u32 = 6;
/// In other words, this is an upper bound on how many blocks we think it can take us to get a
/// transaction confirmed (and we use it in a few more, equivalent, places).
pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers).
pub(crate) const HTLC_FAIL_TIMEOUT_BLOCKS: u32 = 3;

#[derive(Clone, PartialEq)]
enum KeyStorage {
Expand DownExpand Up@@ -1184,16 +1190,7 @@ impl ChannelMonitor {
}
}
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
let mut needs_broadcast = false;
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
needs_broadcast = true;
}
}
}

if needs_broadcast {
if self.would_broadcast_at_height(height) {
broadcaster.broadcast_transaction(&cur_local_tx.tx);
for tx in self.broadcast_by_local_state(&cur_local_tx) {
broadcaster.broadcast_transaction(&tx);
Expand All@@ -1206,10 +1203,29 @@ impl ChannelMonitor {
pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool {
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
return true;
}
// For inbound HTLCs which we know the preimage for, we have to ensure we hit the
// chain with enough room to claim the HTLC without our counterparty being able to
// time out the HTLC first.
// For outbound HTLCs which our counterparty hasn't failed/claimed, our primary
// concern is being able to claim the corresponding inbound HTLC (on another
// channel) before it expires. In fact, we don't even really care if our
// counterparty here claims such an outbound HTLC after it expired as long as we
// can still claim the corresponding HTLC. Thus, to avoid needlessly hitting the
// chain when our counterparty is waiting for expiration to off-chain fail an HTLC
// we give ourselves a few blocks of headroom after expiration before going
// on-chain for an expired HTLC.
// Note that, to avoid a potential attack whereby a node delays claiming an HTLC
// from us until we've reached the point where we go on-chain with the
// corresponding inbound HTLC, we must ensure that outbound HTLCs go on chain at
// least CLTV_CLAIM_BUFFER blocks prior to the inbound HTLC.
// aka outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS == height - CLTV_CLAIM_BUFFER
// inbound_cltv == height + CLTV_CLAIM_BUFFER
// outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS + CLTV_CLAIM_BUFER <= inbound_cltv - CLTV_CLAIM_BUFFER
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= inbound_cltv - outbound_cltv
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= CLTV_EXPIRY_DELTA
if ( htlc.offered && htlc.cltv_expiry + HTLC_FAIL_TIMEOUT_BLOCKS <= height) ||
(!htlc.offered && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) {
return true;
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/ln/msgs.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -485,7 +485,19 @@ pub enum HTLCFailChannelUpdate {
ChannelClosed {
/// The short_channel_id which has now closed.
short_channel_id: u64,
/// when this true, this channel should be permanently removed from the
/// consideration. Otherwise, this channel can be restored as new channel_update is received
is_permanent: bool,
},
/// We received an error which indicated only that a node has failed
NodeFailure {
/// The node_id that has failed.
node_id: PublicKey,
/// when this true, node should be permanently removed from the
/// consideration. Otherwise, the channels connected to this node can be
/// restored as new channel_update is received
is_permanent: bool,
}
}

/// A trait to describe an object which can receive channel messages.
Expand DownExpand Up@@ -517,7 +529,7 @@ pub trait ChannelMessageHandler : events::EventsProvider + Send + Sync {
/// Handle an incoming update_fulfill_htlc message from the given peer.
fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_htlc message from the given peer.
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<Option<HTLCFailChannelUpdate>, HandleError>;
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_malformed_htlc message from the given peer.
fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC) -> Result<(), HandleError>;
/// Handle an incoming commitment_signed message from the given peer.
Expand Down
9 changes: 5 additions & 4 deletions src/ln/peer_handler.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -615,10 +615,7 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
},
131 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailHTLC::read(&mut reader));
let chan_update = try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
if let Some(update) = chan_update {
self.message_handler.route_handler.handle_htlc_fail_channel_update(&update);
}
try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
},
135 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailMalformedHTLC::read(&mut reader));
Expand DownExpand Up@@ -906,6 +903,10 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
}
continue;
},
Event::RouteUpdate { ref update } => {
self.message_handler.route_handler.handle_htlc_fail_channel_update(update);
continue;
},
Event::HandleError { ref node_id, ref action } => {
if let Some(ref action) = *action {
match *action {
Expand Down
20 changes: 19 additions & 1 deletion src/ln/router.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,9 @@ pub struct RouteHop {
/// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value
/// expected at the destination, in excess of the current block height.
pub cltv_expiry_delta: u32,
/// channel update timestamp of the outbound channel of each hop when this route is
/// calculated
pub channel_update_timestamp: u32,
}

/// A route from us through the network to a destination
Expand DownExpand Up@@ -168,6 +171,8 @@ impl NetworkMap {
pub struct RouteHint {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
/// last update
pub last_update: u32,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The static msat-denominated fee which must be paid to use this channel
Expand DownExpand Up@@ -349,12 +354,17 @@ impl RoutingMessageHandler for Router {
&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } => {
let _ = self.handle_channel_update(msg);
},
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => {
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id, is_permanent:_ } => {
let mut network = self.network_map.write().unwrap();
if let Some(chan) = network.channels.remove(short_channel_id) {
Self::remove_channel_in_nodes(&mut network.nodes, &chan, *short_channel_id);
}
},
&msgs::HTLCFailChannelUpdate::NodeFailure { ref node_id, is_permanent:_ } => {
//let mut network = self.network_map.write().unwrap();
//TODO: check _blamed_upstream_node
self.mark_node_bad(node_id, false);
},
}
}

Expand DownExpand Up@@ -450,6 +460,7 @@ impl cmp::PartialOrd for RouteGraphNode {

struct DummyDirectionalChannelInfo {
src_node_id: PublicKey,
last_update: u32,
cltv_expiry_delta: u32,
htlc_minimum_msat: u64,
fee_base_msat: u32,
Expand DownExpand Up@@ -560,6 +571,7 @@ impl Router {

let dummy_directional_info = DummyDirectionalChannelInfo { // used for first_hops routes
src_node_id: network.our_node_id.clone(),
last_update: 0,
cltv_expiry_delta: 0,
htlc_minimum_msat: 0,
fee_base_msat: 0,
Expand All@@ -580,6 +592,7 @@ impl Router {
short_channel_id,
fee_msat: final_value_msat,
cltv_expiry_delta: final_cltv,
channel_update_timestamp: 0, //TODO: related to local channel_update_handling?
}],
});
}
Expand DownExpand Up@@ -613,6 +626,7 @@ impl Router {
short_channel_id: 0,
fee_msat: 0,
cltv_expiry_delta: 0,
channel_update_timestamp: $directional_info.last_update,
})
});
if $directional_info.src_node_id != network.our_node_id {
Expand All@@ -639,6 +653,7 @@ impl Router {
short_channel_id: $chan_id.clone(),
fee_msat: new_fee, // This field is ignored on the last-hop anyway
cltv_expiry_delta: $directional_info.cltv_expiry_delta as u32,
channel_update_timestamp: $directional_info.last_update,
}
}
}
Expand DownExpand Up@@ -1161,20 +1176,23 @@ mod tests {
let mut last_hops = vec!(RouteHint {
src_node_id: node4.clone(),
short_channel_id: 8,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (8 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node5.clone(),
short_channel_id: 9,
last_update: 0,
fee_base_msat: 1001,
fee_proportional_millionths: 0,
cltv_expiry_delta: (9 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node6.clone(),
short_channel_id: 10,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (10 << 8) | 1,
Expand Down
11 changes: 11 additions & 0 deletions src/util/events.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,6 +75,10 @@ pub enum Event {
PaymentFailed {
/// The hash which was given to ChannelManager::send_payment.
payment_hash: [u8; 32],
/// Inindicates if the payment can be retried.
retryable: bool,
/// Error code from Onion failure message
error_code: Option<u16>,
},
/// Used to indicate that ChannelManager::process_pending_htlc_forwards should be called at a
/// time in the future.
Expand DownExpand Up@@ -161,6 +165,13 @@ pub enum Event {
node_id: PublicKey,
/// The action which should be taken.
action: Option<msgs::ErrorAction>
},

/// Used to indicate a change should be made into network map
///
RouteUpdate {
/// The channel/node update which should be sent to router
update: msgs::HTLCFailChannelUpdate,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/util/test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -110,7 +110,7 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFulfillHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<Option<msgs::HTLCFailChannelUpdate>, HandleError> {
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailMalformedHTLC) -> Result<(), HandleError> {
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fill out failure codes for onion packet by yuntai · Pull Request #157 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
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
3 changes: 2 additions & 1 deletion fuzz/fuzz_targets/router_target.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -187,7 +187,7 @@ pub fn do_test(data: &[u8]) {
},
1 => {
let short_channel_id = slice_to_be64(get_slice!(8));
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id});
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id, is_permanent: false});
},
_ => return,
}
Expand DownExpand Up@@ -223,6 +223,7 @@ pub fn do_test(data: &[u8]) {
fee_proportional_millionths: slice_to_be32(get_slice!(4)),
cltv_expiry_delta: slice_to_be16(get_slice!(2)),
htlc_minimum_msat: slice_to_be64(get_slice!(8)),
last_update: 0,
});
}
&last_hops_vec[..]
Expand Down
11 changes: 8 additions & 3 deletions src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1545,7 +1545,7 @@ impl Channel {
Ok(())
}

/// Removes an outbound HTLC which has been commitment_signed by the remote end
/// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
#[inline]
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
for htlc in self.pending_outbound_htlcs.iter_mut() {
Expand All@@ -1559,13 +1559,13 @@ impl Channel {
};
match htlc.state {
OutboundHTLCState::LocalAnnounced(_) =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")),
OutboundHTLCState::Committed => {
htlc.state = OutboundHTLCState::RemoteRemoved;
htlc.fail_reason = fail_reason;
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")),
}
return Ok(&htlc.source);
}
Expand DownExpand Up@@ -2450,6 +2450,11 @@ impl Channel {
self.our_htlc_minimum_msat
}

/// Allowed in any state (including after shutdown)
pub fn get_their_htlc_minimum_msat(&self) -> u64 {
self.our_htlc_minimum_msat
}

pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
Expand Down
733 changes: 618 additions & 115 deletions src/ln/channelmanager.rs

Large diffs are not rendered by default.

46 changes: 31 additions & 15 deletions src/ln/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,13 @@ impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
const CLTV_SHARED_CLAIM_BUFFER: u32 = 12;
/// If an HTLC expires within this many blocks, force-close the channel to broadcast the
/// HTLC-Success transaction.
const CLTV_CLAIM_BUFFER: u32 = 6;
/// In other words, this is an upper bound on how many blocks we think it can take us to get a
/// transaction confirmed (and we use it in a few more, equivalent, places).
pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers).
pub(crate) const HTLC_FAIL_TIMEOUT_BLOCKS: u32 = 3;

#[derive(Clone, PartialEq)]
enum KeyStorage {
Expand DownExpand Up@@ -1184,16 +1190,7 @@ impl ChannelMonitor {
}
}
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
let mut needs_broadcast = false;
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
needs_broadcast = true;
}
}
}

if needs_broadcast {
if self.would_broadcast_at_height(height) {
broadcaster.broadcast_transaction(&cur_local_tx.tx);
for tx in self.broadcast_by_local_state(&cur_local_tx) {
broadcaster.broadcast_transaction(&tx);
Expand All@@ -1206,10 +1203,29 @@ impl ChannelMonitor {
pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool {
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
return true;
}
// For inbound HTLCs which we know the preimage for, we have to ensure we hit the
// chain with enough room to claim the HTLC without our counterparty being able to
// time out the HTLC first.
// For outbound HTLCs which our counterparty hasn't failed/claimed, our primary
// concern is being able to claim the corresponding inbound HTLC (on another
// channel) before it expires. In fact, we don't even really care if our
// counterparty here claims such an outbound HTLC after it expired as long as we
// can still claim the corresponding HTLC. Thus, to avoid needlessly hitting the
// chain when our counterparty is waiting for expiration to off-chain fail an HTLC
// we give ourselves a few blocks of headroom after expiration before going
// on-chain for an expired HTLC.
// Note that, to avoid a potential attack whereby a node delays claiming an HTLC
// from us until we've reached the point where we go on-chain with the
// corresponding inbound HTLC, we must ensure that outbound HTLCs go on chain at
// least CLTV_CLAIM_BUFFER blocks prior to the inbound HTLC.
// aka outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS == height - CLTV_CLAIM_BUFFER
// inbound_cltv == height + CLTV_CLAIM_BUFFER
// outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS + CLTV_CLAIM_BUFER <= inbound_cltv - CLTV_CLAIM_BUFFER
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= inbound_cltv - outbound_cltv
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= CLTV_EXPIRY_DELTA
if ( htlc.offered && htlc.cltv_expiry + HTLC_FAIL_TIMEOUT_BLOCKS <= height) ||
(!htlc.offered && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) {
return true;
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/ln/msgs.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -485,7 +485,19 @@ pub enum HTLCFailChannelUpdate {
ChannelClosed {
/// The short_channel_id which has now closed.
short_channel_id: u64,
/// when this true, this channel should be permanently removed from the
/// consideration. Otherwise, this channel can be restored as new channel_update is received
is_permanent: bool,
},
/// We received an error which indicated only that a node has failed
NodeFailure {
/// The node_id that has failed.
node_id: PublicKey,
/// when this true, node should be permanently removed from the
/// consideration. Otherwise, the channels connected to this node can be
/// restored as new channel_update is received
is_permanent: bool,
}
}

/// A trait to describe an object which can receive channel messages.
Expand DownExpand Up@@ -517,7 +529,7 @@ pub trait ChannelMessageHandler : events::EventsProvider + Send + Sync {
/// Handle an incoming update_fulfill_htlc message from the given peer.
fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_htlc message from the given peer.
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<Option<HTLCFailChannelUpdate>, HandleError>;
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_malformed_htlc message from the given peer.
fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC) -> Result<(), HandleError>;
/// Handle an incoming commitment_signed message from the given peer.
Expand Down
9 changes: 5 additions & 4 deletions src/ln/peer_handler.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -615,10 +615,7 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
},
131 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailHTLC::read(&mut reader));
let chan_update = try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
if let Some(update) = chan_update {
self.message_handler.route_handler.handle_htlc_fail_channel_update(&update);
}
try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
},
135 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailMalformedHTLC::read(&mut reader));
Expand DownExpand Up@@ -906,6 +903,10 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
}
continue;
},
Event::RouteUpdate { ref update } => {
self.message_handler.route_handler.handle_htlc_fail_channel_update(update);
continue;
},
Event::HandleError { ref node_id, ref action } => {
if let Some(ref action) = *action {
match *action {
Expand Down
20 changes: 19 additions & 1 deletion src/ln/router.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,9 @@ pub struct RouteHop {
/// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value
/// expected at the destination, in excess of the current block height.
pub cltv_expiry_delta: u32,
/// channel update timestamp of the outbound channel of each hop when this route is
/// calculated
pub channel_update_timestamp: u32,
}

/// A route from us through the network to a destination
Expand DownExpand Up@@ -168,6 +171,8 @@ impl NetworkMap {
pub struct RouteHint {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
/// last update
pub last_update: u32,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The static msat-denominated fee which must be paid to use this channel
Expand DownExpand Up@@ -349,12 +354,17 @@ impl RoutingMessageHandler for Router {
&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } => {
let _ = self.handle_channel_update(msg);
},
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => {
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id, is_permanent:_ } => {
let mut network = self.network_map.write().unwrap();
if let Some(chan) = network.channels.remove(short_channel_id) {
Self::remove_channel_in_nodes(&mut network.nodes, &chan, *short_channel_id);
}
},
&msgs::HTLCFailChannelUpdate::NodeFailure { ref node_id, is_permanent:_ } => {
//let mut network = self.network_map.write().unwrap();
//TODO: check _blamed_upstream_node
self.mark_node_bad(node_id, false);
},
}
}

Expand DownExpand Up@@ -450,6 +460,7 @@ impl cmp::PartialOrd for RouteGraphNode {

struct DummyDirectionalChannelInfo {
src_node_id: PublicKey,
last_update: u32,
cltv_expiry_delta: u32,
htlc_minimum_msat: u64,
fee_base_msat: u32,
Expand DownExpand Up@@ -560,6 +571,7 @@ impl Router {

let dummy_directional_info = DummyDirectionalChannelInfo { // used for first_hops routes
src_node_id: network.our_node_id.clone(),
last_update: 0,
cltv_expiry_delta: 0,
htlc_minimum_msat: 0,
fee_base_msat: 0,
Expand All@@ -580,6 +592,7 @@ impl Router {
short_channel_id,
fee_msat: final_value_msat,
cltv_expiry_delta: final_cltv,
channel_update_timestamp: 0, //TODO: related to local channel_update_handling?
}],
});
}
Expand DownExpand Up@@ -613,6 +626,7 @@ impl Router {
short_channel_id: 0,
fee_msat: 0,
cltv_expiry_delta: 0,
channel_update_timestamp: $directional_info.last_update,
})
});
if $directional_info.src_node_id != network.our_node_id {
Expand All@@ -639,6 +653,7 @@ impl Router {
short_channel_id: $chan_id.clone(),
fee_msat: new_fee, // This field is ignored on the last-hop anyway
cltv_expiry_delta: $directional_info.cltv_expiry_delta as u32,
channel_update_timestamp: $directional_info.last_update,
}
}
}
Expand DownExpand Up@@ -1161,20 +1176,23 @@ mod tests {
let mut last_hops = vec!(RouteHint {
src_node_id: node4.clone(),
short_channel_id: 8,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (8 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node5.clone(),
short_channel_id: 9,
last_update: 0,
fee_base_msat: 1001,
fee_proportional_millionths: 0,
cltv_expiry_delta: (9 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node6.clone(),
short_channel_id: 10,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (10 << 8) | 1,
Expand Down
11 changes: 11 additions & 0 deletions src/util/events.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,6 +75,10 @@ pub enum Event {
PaymentFailed {
/// The hash which was given to ChannelManager::send_payment.
payment_hash: [u8; 32],
/// Inindicates if the payment can be retried.
retryable: bool,
/// Error code from Onion failure message
error_code: Option<u16>,
},
/// Used to indicate that ChannelManager::process_pending_htlc_forwards should be called at a
/// time in the future.
Expand DownExpand Up@@ -161,6 +165,13 @@ pub enum Event {
node_id: PublicKey,
/// The action which should be taken.
action: Option<msgs::ErrorAction>
},

/// Used to indicate a change should be made into network map
///
RouteUpdate {
/// The channel/node update which should be sent to router
update: msgs::HTLCFailChannelUpdate,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/util/test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -110,7 +110,7 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFulfillHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<Option<msgs::HTLCFailChannelUpdate>, HandleError> {
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailMalformedHTLC) -> Result<(), HandleError> {
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fill out failure codes for onion packet by yuntai · Pull Request #157 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
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
3 changes: 2 additions & 1 deletion fuzz/fuzz_targets/router_target.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -187,7 +187,7 @@ pub fn do_test(data: &[u8]) {
},
1 => {
let short_channel_id = slice_to_be64(get_slice!(8));
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id});
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id, is_permanent: false});
},
_ => return,
}
Expand DownExpand Up@@ -223,6 +223,7 @@ pub fn do_test(data: &[u8]) {
fee_proportional_millionths: slice_to_be32(get_slice!(4)),
cltv_expiry_delta: slice_to_be16(get_slice!(2)),
htlc_minimum_msat: slice_to_be64(get_slice!(8)),
last_update: 0,
});
}
&last_hops_vec[..]
Expand Down
11 changes: 8 additions & 3 deletions src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1545,7 +1545,7 @@ impl Channel {
Ok(())
}

/// Removes an outbound HTLC which has been commitment_signed by the remote end
/// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
#[inline]
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
for htlc in self.pending_outbound_htlcs.iter_mut() {
Expand All@@ -1559,13 +1559,13 @@ impl Channel {
};
match htlc.state {
OutboundHTLCState::LocalAnnounced(_) =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")),
OutboundHTLCState::Committed => {
htlc.state = OutboundHTLCState::RemoteRemoved;
htlc.fail_reason = fail_reason;
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")),
}
return Ok(&htlc.source);
}
Expand DownExpand Up@@ -2450,6 +2450,11 @@ impl Channel {
self.our_htlc_minimum_msat
}

/// Allowed in any state (including after shutdown)
pub fn get_their_htlc_minimum_msat(&self) -> u64 {
self.our_htlc_minimum_msat
}

pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
Expand Down
733 changes: 618 additions & 115 deletions src/ln/channelmanager.rs

Large diffs are not rendered by default.

46 changes: 31 additions & 15 deletions src/ln/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,13 @@ impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
const CLTV_SHARED_CLAIM_BUFFER: u32 = 12;
/// If an HTLC expires within this many blocks, force-close the channel to broadcast the
/// HTLC-Success transaction.
const CLTV_CLAIM_BUFFER: u32 = 6;
/// In other words, this is an upper bound on how many blocks we think it can take us to get a
/// transaction confirmed (and we use it in a few more, equivalent, places).
pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers).
pub(crate) const HTLC_FAIL_TIMEOUT_BLOCKS: u32 = 3;

#[derive(Clone, PartialEq)]
enum KeyStorage {
Expand DownExpand Up@@ -1184,16 +1190,7 @@ impl ChannelMonitor {
}
}
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
let mut needs_broadcast = false;
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
needs_broadcast = true;
}
}
}

if needs_broadcast {
if self.would_broadcast_at_height(height) {
broadcaster.broadcast_transaction(&cur_local_tx.tx);
for tx in self.broadcast_by_local_state(&cur_local_tx) {
broadcaster.broadcast_transaction(&tx);
Expand All@@ -1206,10 +1203,29 @@ impl ChannelMonitor {
pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool {
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
return true;
}
// For inbound HTLCs which we know the preimage for, we have to ensure we hit the
// chain with enough room to claim the HTLC without our counterparty being able to
// time out the HTLC first.
// For outbound HTLCs which our counterparty hasn't failed/claimed, our primary
// concern is being able to claim the corresponding inbound HTLC (on another
// channel) before it expires. In fact, we don't even really care if our
// counterparty here claims such an outbound HTLC after it expired as long as we
// can still claim the corresponding HTLC. Thus, to avoid needlessly hitting the
// chain when our counterparty is waiting for expiration to off-chain fail an HTLC
// we give ourselves a few blocks of headroom after expiration before going
// on-chain for an expired HTLC.
// Note that, to avoid a potential attack whereby a node delays claiming an HTLC
// from us until we've reached the point where we go on-chain with the
// corresponding inbound HTLC, we must ensure that outbound HTLCs go on chain at
// least CLTV_CLAIM_BUFFER blocks prior to the inbound HTLC.
// aka outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS == height - CLTV_CLAIM_BUFFER
// inbound_cltv == height + CLTV_CLAIM_BUFFER
// outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS + CLTV_CLAIM_BUFER <= inbound_cltv - CLTV_CLAIM_BUFFER
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= inbound_cltv - outbound_cltv
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= CLTV_EXPIRY_DELTA
if ( htlc.offered && htlc.cltv_expiry + HTLC_FAIL_TIMEOUT_BLOCKS <= height) ||
(!htlc.offered && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) {
return true;
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/ln/msgs.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -485,7 +485,19 @@ pub enum HTLCFailChannelUpdate {
ChannelClosed {
/// The short_channel_id which has now closed.
short_channel_id: u64,
/// when this true, this channel should be permanently removed from the
/// consideration. Otherwise, this channel can be restored as new channel_update is received
is_permanent: bool,
},
/// We received an error which indicated only that a node has failed
NodeFailure {
/// The node_id that has failed.
node_id: PublicKey,
/// when this true, node should be permanently removed from the
/// consideration. Otherwise, the channels connected to this node can be
/// restored as new channel_update is received
is_permanent: bool,
}
}

/// A trait to describe an object which can receive channel messages.
Expand DownExpand Up@@ -517,7 +529,7 @@ pub trait ChannelMessageHandler : events::EventsProvider + Send + Sync {
/// Handle an incoming update_fulfill_htlc message from the given peer.
fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_htlc message from the given peer.
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<Option<HTLCFailChannelUpdate>, HandleError>;
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_malformed_htlc message from the given peer.
fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC) -> Result<(), HandleError>;
/// Handle an incoming commitment_signed message from the given peer.
Expand Down
9 changes: 5 additions & 4 deletions src/ln/peer_handler.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -615,10 +615,7 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
},
131 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailHTLC::read(&mut reader));
let chan_update = try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
if let Some(update) = chan_update {
self.message_handler.route_handler.handle_htlc_fail_channel_update(&update);
}
try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
},
135 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailMalformedHTLC::read(&mut reader));
Expand DownExpand Up@@ -906,6 +903,10 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
}
continue;
},
Event::RouteUpdate { ref update } => {
self.message_handler.route_handler.handle_htlc_fail_channel_update(update);
continue;
},
Event::HandleError { ref node_id, ref action } => {
if let Some(ref action) = *action {
match *action {
Expand Down
20 changes: 19 additions & 1 deletion src/ln/router.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,9 @@ pub struct RouteHop {
/// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value
/// expected at the destination, in excess of the current block height.
pub cltv_expiry_delta: u32,
/// channel update timestamp of the outbound channel of each hop when this route is
/// calculated
pub channel_update_timestamp: u32,
}

/// A route from us through the network to a destination
Expand DownExpand Up@@ -168,6 +171,8 @@ impl NetworkMap {
pub struct RouteHint {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
/// last update
pub last_update: u32,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The static msat-denominated fee which must be paid to use this channel
Expand DownExpand Up@@ -349,12 +354,17 @@ impl RoutingMessageHandler for Router {
&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } => {
let _ = self.handle_channel_update(msg);
},
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => {
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id, is_permanent:_ } => {
let mut network = self.network_map.write().unwrap();
if let Some(chan) = network.channels.remove(short_channel_id) {
Self::remove_channel_in_nodes(&mut network.nodes, &chan, *short_channel_id);
}
},
&msgs::HTLCFailChannelUpdate::NodeFailure { ref node_id, is_permanent:_ } => {
//let mut network = self.network_map.write().unwrap();
//TODO: check _blamed_upstream_node
self.mark_node_bad(node_id, false);
},
}
}

Expand DownExpand Up@@ -450,6 +460,7 @@ impl cmp::PartialOrd for RouteGraphNode {

struct DummyDirectionalChannelInfo {
src_node_id: PublicKey,
last_update: u32,
cltv_expiry_delta: u32,
htlc_minimum_msat: u64,
fee_base_msat: u32,
Expand DownExpand Up@@ -560,6 +571,7 @@ impl Router {

let dummy_directional_info = DummyDirectionalChannelInfo { // used for first_hops routes
src_node_id: network.our_node_id.clone(),
last_update: 0,
cltv_expiry_delta: 0,
htlc_minimum_msat: 0,
fee_base_msat: 0,
Expand All@@ -580,6 +592,7 @@ impl Router {
short_channel_id,
fee_msat: final_value_msat,
cltv_expiry_delta: final_cltv,
channel_update_timestamp: 0, //TODO: related to local channel_update_handling?
}],
});
}
Expand DownExpand Up@@ -613,6 +626,7 @@ impl Router {
short_channel_id: 0,
fee_msat: 0,
cltv_expiry_delta: 0,
channel_update_timestamp: $directional_info.last_update,
})
});
if $directional_info.src_node_id != network.our_node_id {
Expand All@@ -639,6 +653,7 @@ impl Router {
short_channel_id: $chan_id.clone(),
fee_msat: new_fee, // This field is ignored on the last-hop anyway
cltv_expiry_delta: $directional_info.cltv_expiry_delta as u32,
channel_update_timestamp: $directional_info.last_update,
}
}
}
Expand DownExpand Up@@ -1161,20 +1176,23 @@ mod tests {
let mut last_hops = vec!(RouteHint {
src_node_id: node4.clone(),
short_channel_id: 8,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (8 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node5.clone(),
short_channel_id: 9,
last_update: 0,
fee_base_msat: 1001,
fee_proportional_millionths: 0,
cltv_expiry_delta: (9 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node6.clone(),
short_channel_id: 10,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (10 << 8) | 1,
Expand Down
11 changes: 11 additions & 0 deletions src/util/events.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,6 +75,10 @@ pub enum Event {
PaymentFailed {
/// The hash which was given to ChannelManager::send_payment.
payment_hash: [u8; 32],
/// Inindicates if the payment can be retried.
retryable: bool,
/// Error code from Onion failure message
error_code: Option<u16>,
},
/// Used to indicate that ChannelManager::process_pending_htlc_forwards should be called at a
/// time in the future.
Expand DownExpand Up@@ -161,6 +165,13 @@ pub enum Event {
node_id: PublicKey,
/// The action which should be taken.
action: Option<msgs::ErrorAction>
},

/// Used to indicate a change should be made into network map
///
RouteUpdate {
/// The channel/node update which should be sent to router
update: msgs::HTLCFailChannelUpdate,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/util/test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -110,7 +110,7 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFulfillHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<Option<msgs::HTLCFailChannelUpdate>, HandleError> {
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailMalformedHTLC) -> Result<(), HandleError> {
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Fill out failure codes for onion packet by yuntai · Pull Request #157 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
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
3 changes: 2 additions & 1 deletion fuzz/fuzz_targets/router_target.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -187,7 +187,7 @@ pub fn do_test(data: &[u8]) {
},
1 => {
let short_channel_id = slice_to_be64(get_slice!(8));
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id});
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id, is_permanent: false});
},
_ => return,
}
Expand DownExpand Up@@ -223,6 +223,7 @@ pub fn do_test(data: &[u8]) {
fee_proportional_millionths: slice_to_be32(get_slice!(4)),
cltv_expiry_delta: slice_to_be16(get_slice!(2)),
htlc_minimum_msat: slice_to_be64(get_slice!(8)),
last_update: 0,
});
}
&last_hops_vec[..]
Expand Down
11 changes: 8 additions & 3 deletions src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1545,7 +1545,7 @@ impl Channel {
Ok(())
}

/// Removes an outbound HTLC which has been commitment_signed by the remote end
/// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
#[inline]
fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
for htlc in self.pending_outbound_htlcs.iter_mut() {
Expand All@@ -1559,13 +1559,13 @@ impl Channel {
};
match htlc.state {
OutboundHTLCState::LocalAnnounced(_) =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")),
OutboundHTLCState::Committed => {
htlc.state = OutboundHTLCState::RemoteRemoved;
htlc.fail_reason = fail_reason;
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")),
}
return Ok(&htlc.source);
}
Expand DownExpand Up@@ -2450,6 +2450,11 @@ impl Channel {
self.our_htlc_minimum_msat
}

/// Allowed in any state (including after shutdown)
pub fn get_their_htlc_minimum_msat(&self) -> u64 {
self.our_htlc_minimum_msat
}

pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
Expand Down
733 changes: 618 additions & 115 deletions src/ln/channelmanager.rs

Large diffs are not rendered by default.

46 changes: 31 additions & 15 deletions src/ln/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,13 @@ impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
const CLTV_SHARED_CLAIM_BUFFER: u32 = 12;
/// If an HTLC expires within this many blocks, force-close the channel to broadcast the
/// HTLC-Success transaction.
const CLTV_CLAIM_BUFFER: u32 = 6;
/// In other words, this is an upper bound on how many blocks we think it can take us to get a
/// transaction confirmed (and we use it in a few more, equivalent, places).
pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers).
pub(crate) const HTLC_FAIL_TIMEOUT_BLOCKS: u32 = 3;

#[derive(Clone, PartialEq)]
enum KeyStorage {
Expand DownExpand Up@@ -1184,16 +1190,7 @@ impl ChannelMonitor {
}
}
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
let mut needs_broadcast = false;
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
needs_broadcast = true;
}
}
}

if needs_broadcast {
if self.would_broadcast_at_height(height) {
broadcaster.broadcast_transaction(&cur_local_tx.tx);
for tx in self.broadcast_by_local_state(&cur_local_tx) {
broadcaster.broadcast_transaction(&tx);
Expand All@@ -1206,10 +1203,29 @@ impl ChannelMonitor {
pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool {
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
if htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER {
if htlc.offered || self.payment_preimages.contains_key(&htlc.payment_hash) {
return true;
}
// For inbound HTLCs which we know the preimage for, we have to ensure we hit the
// chain with enough room to claim the HTLC without our counterparty being able to
// time out the HTLC first.
// For outbound HTLCs which our counterparty hasn't failed/claimed, our primary
// concern is being able to claim the corresponding inbound HTLC (on another
// channel) before it expires. In fact, we don't even really care if our
// counterparty here claims such an outbound HTLC after it expired as long as we
// can still claim the corresponding HTLC. Thus, to avoid needlessly hitting the
// chain when our counterparty is waiting for expiration to off-chain fail an HTLC
// we give ourselves a few blocks of headroom after expiration before going
// on-chain for an expired HTLC.
// Note that, to avoid a potential attack whereby a node delays claiming an HTLC
// from us until we've reached the point where we go on-chain with the
// corresponding inbound HTLC, we must ensure that outbound HTLCs go on chain at
// least CLTV_CLAIM_BUFFER blocks prior to the inbound HTLC.
// aka outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS == height - CLTV_CLAIM_BUFFER
// inbound_cltv == height + CLTV_CLAIM_BUFFER
// outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS + CLTV_CLAIM_BUFER <= inbound_cltv - CLTV_CLAIM_BUFFER
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= inbound_cltv - outbound_cltv
// HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= CLTV_EXPIRY_DELTA
if ( htlc.offered && htlc.cltv_expiry + HTLC_FAIL_TIMEOUT_BLOCKS <= height) ||
(!htlc.offered && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) {
return true;
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/ln/msgs.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -485,7 +485,19 @@ pub enum HTLCFailChannelUpdate {
ChannelClosed {
/// The short_channel_id which has now closed.
short_channel_id: u64,
/// when this true, this channel should be permanently removed from the
/// consideration. Otherwise, this channel can be restored as new channel_update is received
is_permanent: bool,
},
/// We received an error which indicated only that a node has failed
NodeFailure {
/// The node_id that has failed.
node_id: PublicKey,
/// when this true, node should be permanently removed from the
/// consideration. Otherwise, the channels connected to this node can be
/// restored as new channel_update is received
is_permanent: bool,
}
}

/// A trait to describe an object which can receive channel messages.
Expand DownExpand Up@@ -517,7 +529,7 @@ pub trait ChannelMessageHandler : events::EventsProvider + Send + Sync {
/// Handle an incoming update_fulfill_htlc message from the given peer.
fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_htlc message from the given peer.
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<Option<HTLCFailChannelUpdate>, HandleError>;
fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<(), HandleError>;
/// Handle an incoming update_fail_malformed_htlc message from the given peer.
fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC) -> Result<(), HandleError>;
/// Handle an incoming commitment_signed message from the given peer.
Expand Down
9 changes: 5 additions & 4 deletions src/ln/peer_handler.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -615,10 +615,7 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
},
131 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailHTLC::read(&mut reader));
let chan_update = try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
if let Some(update) = chan_update {
self.message_handler.route_handler.handle_htlc_fail_channel_update(&update);
}
try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg));
},
135 => {
let msg = try_potential_decodeerror!(msgs::UpdateFailMalformedHTLC::read(&mut reader));
Expand DownExpand Up@@ -906,6 +903,10 @@ impl<Descriptor: SocketDescriptor> PeerManager<Descriptor> {
}
continue;
},
Event::RouteUpdate { ref update } => {
self.message_handler.route_handler.handle_htlc_fail_channel_update(update);
continue;
},
Event::HandleError { ref node_id, ref action } => {
if let Some(ref action) = *action {
match *action {
Expand Down
20 changes: 19 additions & 1 deletion src/ln/router.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,9 @@ pub struct RouteHop {
/// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value
/// expected at the destination, in excess of the current block height.
pub cltv_expiry_delta: u32,
/// channel update timestamp of the outbound channel of each hop when this route is
/// calculated
pub channel_update_timestamp: u32,
}

/// A route from us through the network to a destination
Expand DownExpand Up@@ -168,6 +171,8 @@ impl NetworkMap {
pub struct RouteHint {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
/// last update
pub last_update: u32,
/// The short_channel_id of this channel
pub short_channel_id: u64,
/// The static msat-denominated fee which must be paid to use this channel
Expand DownExpand Up@@ -349,12 +354,17 @@ impl RoutingMessageHandler for Router {
&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } => {
let _ = self.handle_channel_update(msg);
},
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => {
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id, is_permanent:_ } => {
let mut network = self.network_map.write().unwrap();
if let Some(chan) = network.channels.remove(short_channel_id) {
Self::remove_channel_in_nodes(&mut network.nodes, &chan, *short_channel_id);
}
},
&msgs::HTLCFailChannelUpdate::NodeFailure { ref node_id, is_permanent:_ } => {
//let mut network = self.network_map.write().unwrap();
//TODO: check _blamed_upstream_node
self.mark_node_bad(node_id, false);
},
}
}

Expand DownExpand Up@@ -450,6 +460,7 @@ impl cmp::PartialOrd for RouteGraphNode {

struct DummyDirectionalChannelInfo {
src_node_id: PublicKey,
last_update: u32,
cltv_expiry_delta: u32,
htlc_minimum_msat: u64,
fee_base_msat: u32,
Expand DownExpand Up@@ -560,6 +571,7 @@ impl Router {

let dummy_directional_info = DummyDirectionalChannelInfo { // used for first_hops routes
src_node_id: network.our_node_id.clone(),
last_update: 0,
cltv_expiry_delta: 0,
htlc_minimum_msat: 0,
fee_base_msat: 0,
Expand All@@ -580,6 +592,7 @@ impl Router {
short_channel_id,
fee_msat: final_value_msat,
cltv_expiry_delta: final_cltv,
channel_update_timestamp: 0, //TODO: related to local channel_update_handling?
}],
});
}
Expand DownExpand Up@@ -613,6 +626,7 @@ impl Router {
short_channel_id: 0,
fee_msat: 0,
cltv_expiry_delta: 0,
channel_update_timestamp: $directional_info.last_update,
})
});
if $directional_info.src_node_id != network.our_node_id {
Expand All@@ -639,6 +653,7 @@ impl Router {
short_channel_id: $chan_id.clone(),
fee_msat: new_fee, // This field is ignored on the last-hop anyway
cltv_expiry_delta: $directional_info.cltv_expiry_delta as u32,
channel_update_timestamp: $directional_info.last_update,
}
}
}
Expand DownExpand Up@@ -1161,20 +1176,23 @@ mod tests {
let mut last_hops = vec!(RouteHint {
src_node_id: node4.clone(),
short_channel_id: 8,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (8 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node5.clone(),
short_channel_id: 9,
last_update: 0,
fee_base_msat: 1001,
fee_proportional_millionths: 0,
cltv_expiry_delta: (9 << 8) | 1,
htlc_minimum_msat: 0,
}, RouteHint {
src_node_id: node6.clone(),
short_channel_id: 10,
last_update: 0,
fee_base_msat: 0,
fee_proportional_millionths: 0,
cltv_expiry_delta: (10 << 8) | 1,
Expand Down
11 changes: 11 additions & 0 deletions src/util/events.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,6 +75,10 @@ pub enum Event {
PaymentFailed {
/// The hash which was given to ChannelManager::send_payment.
payment_hash: [u8; 32],
/// Inindicates if the payment can be retried.
retryable: bool,
/// Error code from Onion failure message
error_code: Option<u16>,
},
/// Used to indicate that ChannelManager::process_pending_htlc_forwards should be called at a
/// time in the future.
Expand DownExpand Up@@ -161,6 +165,13 @@ pub enum Event {
node_id: PublicKey,
/// The action which should be taken.
action: Option<msgs::ErrorAction>
},

/// Used to indicate a change should be made into network map
///
RouteUpdate {
/// The channel/node update which should be sent to router
update: msgs::HTLCFailChannelUpdate,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/util/test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -110,7 +110,7 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFulfillHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<Option<msgs::HTLCFailChannelUpdate>, HandleError> {
fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) -> Result<(), HandleError> {
Err(HandleError { err: "", action: None })
}
fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailMalformedHTLC) -> Result<(), HandleError> {
Expand Down