Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Include height to incorrect_or_unknown_payment_details failure #596

Merged

Conversation

D4nte
Copy link
Contributor

@D4nte D4nte commented Apr 19, 2020

Resolves #586.

To prepare the work on this PR, I reviewed all the instantiations of HTLCFailReason::Reason against the latest BOLT spec and checked whether it includes the spec'd data.

I have not reviewed in details whether or not the failure message was instantiated against the right conditions as the issue only mentioned content.

Edit: I intend to leverage type safety with a follow-up PR as per Matt's comment.

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, in the future we should probably have structs in onion_utils which ensure that we are always passing the right data for each possible error type instead of the ad-hoc stuff right now. Certainly thats a lot more work, so should probably be a separate PR. As for a test, the following patch adds a test for this:

diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index cec173e5e..03db866ad 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -1851,9 +1851,9 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
 				match &onion_error {
 					&HTLCFailReason::LightningError { ref err } => {
 #[cfg(test)]
-						let (channel_update, payment_retryable, onion_error_code) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
+						let (channel_update, payment_retryable, onion_error_code, onion_error_data) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
 #[cfg(not(test))]
-						let (channel_update, payment_retryable, _) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
+						let (channel_update, payment_retryable, _, _) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
 						// TODO: If we decided to blame ourselves (or one of our channels) in
 						// process_onion_failure we should close that channel as it implies our
 						// next-hop is needlessly blaming us!
@@ -1869,13 +1869,17 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
 								payment_hash: payment_hash.clone(),
 								rejected_by_dest: !payment_retryable,
 #[cfg(test)]
-								error_code: onion_error_code
+								error_code: onion_error_code,
+#[cfg(test)]
+								error_data: onion_error_data
 							}
 						);
 					},
 					&HTLCFailReason::Reason {
 #[cfg(test)]
 							ref failure_code,
+#[cfg(test)]
+							ref data,
 							.. } => {
 						// we get a fail_malformed_htlc from the first hop
 						// TODO: We'd like to generate a PaymentFailureNetworkUpdate for temporary
@@ -1890,6 +1894,8 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
 								rejected_by_dest: path.len() == 1,
 #[cfg(test)]
 								error_code: Some(*failure_code),
+#[cfg(test)]
+								error_data: Some(data.clone()),
 							}
 						);
 					}
diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs
index 1be967d93..21d868603 100644
--- a/lightning/src/ln/functional_tests.rs
+++ b/lightning/src/ln/functional_tests.rs
@@ -5326,7 +5326,7 @@ fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case:
 
 	let events = nodes[0].node.get_and_clear_pending_events();
 	assert_eq!(events.len(), 1);
-	if let &Event::PaymentFailed { payment_hash:_, ref rejected_by_dest, ref error_code } = &events[0] {
+	if let &Event::PaymentFailed { payment_hash:_, ref rejected_by_dest, ref error_code, error_data: _ } = &events[0] {
 		assert_eq!(*rejected_by_dest, !expected_retryable);
 		assert_eq!(*error_code, expected_error_code);
 	} else {
@@ -6914,9 +6914,20 @@ fn test_check_htlc_underpaying() {
 
 	let events = nodes[0].node.get_and_clear_pending_events();
 	assert_eq!(events.len(), 1);
-	if let &Event::PaymentFailed { payment_hash:_, ref rejected_by_dest, ref error_code } = &events[0] {
+	if let &Event::PaymentFailed { payment_hash:_, ref rejected_by_dest, ref error_code, ref error_data } = &events[0] {
 		assert_eq!(*rejected_by_dest, true);
 		assert_eq!(error_code.unwrap(), 0x4000|15);
+		// 10_000 msat as u64, followed by a height of 99 as u32
+		assert_eq!(&error_data.as_ref().unwrap()[..], &[
+			((10_000u64 >> 7*8) & 0xff) as u8,
+			((10_000u64 >> 6*8) & 0xff) as u8,
+			((10_000u64 >> 5*8) & 0xff) as u8,
+			((10_000u64 >> 4*8) & 0xff) as u8,
+			((10_000u64 >> 3*8) & 0xff) as u8,
+			((10_000u64 >> 2*8) & 0xff) as u8,
+			((10_000u64 >> 1*8) & 0xff) as u8,
+			((10_000u64 >> 0*8) & 0xff) as u8,
+			0, 0, 0, 99]);
 	} else {
 		panic!("Unexpected event");
 	}
diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs
index ff89782be..dc3b2f902 100644
--- a/lightning/src/ln/onion_utils.rs
+++ b/lightning/src/ln/onion_utils.rs
@@ -317,11 +317,13 @@ pub(super) fn build_first_hop_failure_packet(shared_secret: &[u8], failure_type:
 /// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
 /// OutboundRoute).
 /// Returns update, a boolean indicating that the payment itself failed, and the error code.
-pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, logger: &Arc<Logger>, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>) -> (Option<msgs::HTLCFailChannelUpdate>, bool, Option<u16>) {
+#[inline]
+pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, logger: &Arc<Logger>, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>) -> (Option<msgs::HTLCFailChannelUpdate>, bool, Option<u16>, Option<Vec<u8>>) {
 	if let &HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat } = htlc_source {
 		let mut res = None;
 		let mut htlc_msat = *first_hop_htlc_msat;
 		let mut error_code_ret = None;
+		let mut error_packet_ret = None;
 		let mut next_route_hop_ix = 0;
 		let mut is_from_final_node = false;
 
@@ -356,6 +358,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<
 
 						let error_code = byte_utils::slice_to_be16(&error_code_slice);
 						error_code_ret = Some(error_code);
+						error_packet_ret = Some(err_packet.failuremsg[2..].to_vec());
 
 						let (debug_field, debug_field_size) = errors::get_onion_debug_field(error_code);
 
@@ -456,11 +459,11 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<
 			}
 		}).expect("Route that we sent via spontaneously grew invalid keys in the middle of it?");
 		if let Some((channel_update, payment_retryable)) = res {
-			(channel_update, payment_retryable, error_code_ret)
+			(channel_update, payment_retryable, error_code_ret, error_packet_ret)
 		} else {
 			// only not set either packet unparseable or hmac does not match with any
 			// payment not retryable only when garbage is from the final node
-			(None, !is_from_final_node, None)
+			(None, !is_from_final_node, None, None)
 		}
 	} else { unreachable!(); }
 }
diff --git a/lightning/src/util/events.rs b/lightning/src/util/events.rs
index 2c00a1335..8f3460567 100644
--- a/lightning/src/util/events.rs
+++ b/lightning/src/util/events.rs
@@ -96,6 +96,8 @@ pub enum Event {
 		rejected_by_dest: bool,
 #[cfg(test)]
 		error_code: Option<u16>,
+#[cfg(test)]
+		error_data: Option<Vec<u8>>,
 	},
 	/// Used to indicate that ChannelManager::process_pending_htlc_forwards should be called at a
 	/// time in the future.
@@ -142,12 +144,16 @@ impl Writeable for Event {
 			&Event::PaymentFailed { ref payment_hash, ref rejected_by_dest,
 				#[cfg(test)]
 				ref error_code,
+				#[cfg(test)]
+				ref error_data,
 			} => {
 				4u8.write(writer)?;
 				payment_hash.write(writer)?;
 				rejected_by_dest.write(writer)?;
 				#[cfg(test)]
 				error_code.write(writer)?;
+				#[cfg(test)]
+				error_data.write(writer)?;
 			},
 			&Event::PendingHTLCsForwardable { time_forwardable: _ } => {
 				5u8.write(writer)?;
@@ -186,6 +192,8 @@ impl MaybeReadable for Event {
 					rejected_by_dest: Readable::read(reader)?,
 					#[cfg(test)]
 					error_code: Readable::read(reader)?,
+					#[cfg(test)]
+					error_data: Readable::read(reader)?,
 				})),
 			5u8 => Ok(Some(Event::PendingHTLCsForwardable {
 					time_forwardable: Duration::from_secs(0)

lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
@D4nte
Copy link
Contributor Author

D4nte commented Apr 19, 2020

Yea, in the future we should probably have structs in onion_utils which ensure that we are always passing the right data for each possible error type instead of the ad-hoc stuff right now. Certainly thats a lot more work, so should probably be a separate PR.

Yes it's what I thought. I'll aim to do a follow-up PR that does it. Thanks for the test.

@D4nte D4nte force-pushed the 586-update-htlc-error-code-content branch 2 times, most recently from a4d1080 to d55738c Compare April 19, 2020 20:46
D4nte added 2 commits April 20, 2020 08:30
`incorrect_or_unknown_payment_details` failure message,
`0x4000 (PERM) | 15`, should include the following data:
- [u64:htlc_msat]
- [u32:height]
This patches ensure that the height is included in all
the occurrences of this failure message.
Ensure that the best know blockchain height is included in the
data of `incorrect_or_unknown_payment_details` message failure.
@D4nte D4nte force-pushed the 586-update-htlc-error-code-content branch from d55738c to 6e2c9b1 Compare April 19, 2020 22:33
@TheBlueMatt
Copy link
Collaborator

Hmm, please drop the formatting commit. We'll probably want to do that all in one go (#425), its someone needless churn to do it as we go.

@D4nte D4nte force-pushed the 586-update-htlc-error-code-content branch from 6e2c9b1 to 236887d Compare April 19, 2020 23:40
@D4nte
Copy link
Contributor Author

D4nte commented Apr 19, 2020

Hmm, please drop the formatting commit. We'll probably want to do that all in one go (#425), its someone needless churn to do it as we go.

Done!

@TheBlueMatt TheBlueMatt merged commit 900d900 into lightningdevkit:master Apr 20, 2020
@D4nte D4nte deleted the 586-update-htlc-error-code-content branch April 20, 2020 06:56
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Apr 20, 2020
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Apr 20, 2020
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Apr 24, 2020
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request Apr 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update HTLC error code contents
2 participants