lightning/offers/
invoice_error.rs

1// This file is Copyright its original authors, visible in version control
2// history.
3//
4// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7// You may not use this file except in accordance with one or both of these
8// licenses.
9
10//! Data structures and encoding for `invoice_error` messages.
11
12use crate::io;
13use crate::ln::msgs::DecodeError;
14use crate::offers::merkle::SignError;
15use crate::offers::parse::Bolt12SemanticError;
16use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
17use crate::util::string::UntrustedString;
18
19#[allow(unused_imports)]
20use crate::prelude::*;
21
22/// An error in response to an [`InvoiceRequest`] or an [`Bolt12Invoice`].
23///
24/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
25/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
26#[derive(Clone, Debug)]
27#[cfg_attr(test, derive(PartialEq))]
28pub struct InvoiceError {
29	/// The field in the [`InvoiceRequest`] or the [`Bolt12Invoice`] that contained an error.
30	///
31	/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
32	/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
33	pub erroneous_field: Option<ErroneousField>,
34
35	/// An explanation of the error.
36	pub message: UntrustedString,
37}
38
39/// The field in the [`InvoiceRequest`] or the [`Bolt12Invoice`] that contained an error.
40///
41/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
42/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
43#[derive(Clone, Debug)]
44#[cfg_attr(test, derive(PartialEq))]
45pub struct ErroneousField {
46	/// The type number of the TLV field containing the error.
47	pub tlv_fieldnum: u64,
48
49	/// A value to use for the TLV field to avoid the error.
50	pub suggested_value: Option<Vec<u8>>,
51}
52
53impl InvoiceError {
54	/// Creates an [`InvoiceError`] with the given message.
55	pub fn from_string(s: String) -> Self {
56		Self {
57			erroneous_field: None,
58			message: UntrustedString(s),
59		}
60	}
61}
62
63impl core::fmt::Display for InvoiceError {
64	fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
65		self.message.fmt(f)
66	}
67}
68
69impl Writeable for InvoiceError {
70	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
71		let tlv_fieldnum = self.erroneous_field.as_ref().map(|f| f.tlv_fieldnum);
72		let suggested_value =
73			self.erroneous_field.as_ref().and_then(|f| f.suggested_value.as_ref());
74		write_tlv_fields!(writer, {
75			(1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
76			(3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
77			(5, WithoutLength(&self.message), required),
78		});
79		Ok(())
80	}
81}
82
83impl Readable for InvoiceError {
84	fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
85		_init_and_read_len_prefixed_tlv_fields!(reader, {
86			(1, erroneous_field, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
87			(3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
88			(5, error, (option, encoding: (UntrustedString, WithoutLength))),
89		});
90
91		let erroneous_field = match (erroneous_field, suggested_value) {
92			(None, None) => None,
93			(None, Some(_)) => return Err(DecodeError::InvalidValue),
94			(Some(tlv_fieldnum), suggested_value) => {
95				Some(ErroneousField { tlv_fieldnum, suggested_value })
96			},
97		};
98
99		let message = match error {
100			None => return Err(DecodeError::InvalidValue),
101			Some(error) => error,
102		};
103
104		Ok(InvoiceError { erroneous_field, message })
105	}
106}
107
108impl From<Bolt12SemanticError> for InvoiceError {
109	fn from(error: Bolt12SemanticError) -> Self {
110		InvoiceError {
111			erroneous_field: None,
112			message: UntrustedString(format!("{:?}", error)),
113		}
114	}
115}
116
117impl From<SignError> for InvoiceError {
118	fn from(error: SignError) -> Self {
119		let message = match error {
120			SignError::Signing => "Failed signing invoice",
121			SignError::Verification(_) => "Failed invoice signature verification",
122		};
123		InvoiceError {
124			erroneous_field: None,
125			message: UntrustedString(message.to_string()),
126		}
127	}
128}
129
130#[cfg(test)]
131mod tests {
132	use super::{ErroneousField, InvoiceError};
133
134	use crate::ln::msgs::DecodeError;
135	use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, VecWriter, WithoutLength, Writeable};
136	use crate::util::string::UntrustedString;
137
138	#[test]
139	fn parses_invoice_error_without_erroneous_field() {
140		let mut writer = VecWriter(Vec::new());
141		let invoice_error = InvoiceError {
142			erroneous_field: None,
143			message: UntrustedString("Invalid value".to_string()),
144		};
145		invoice_error.write(&mut writer).unwrap();
146
147		let buffer = writer.0;
148		match InvoiceError::read(&mut &buffer[..]) {
149			Ok(invoice_error) => {
150				assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
151				assert_eq!(invoice_error.erroneous_field, None);
152			}
153			Err(e) => panic!("Unexpected error: {:?}", e),
154		}
155	}
156
157	#[test]
158	fn parses_invoice_error_with_erroneous_field() {
159		let mut writer = VecWriter(Vec::new());
160		let invoice_error = InvoiceError {
161			erroneous_field: Some(ErroneousField {
162				tlv_fieldnum: 42,
163				suggested_value: Some(vec![42; 32]),
164			}),
165			message: UntrustedString("Invalid value".to_string()),
166		};
167		invoice_error.write(&mut writer).unwrap();
168
169		let buffer = writer.0;
170		match InvoiceError::read(&mut &buffer[..]) {
171			Ok(invoice_error) => {
172				assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
173				assert_eq!(
174					invoice_error.erroneous_field,
175					Some(ErroneousField { tlv_fieldnum: 42, suggested_value: Some(vec![42; 32]) }),
176				);
177			}
178			Err(e) => panic!("Unexpected error: {:?}", e),
179		}
180	}
181
182	#[test]
183	fn parses_invoice_error_without_suggested_value() {
184		let mut writer = VecWriter(Vec::new());
185		let invoice_error = InvoiceError {
186			erroneous_field: Some(ErroneousField {
187				tlv_fieldnum: 42,
188				suggested_value: None,
189			}),
190			message: UntrustedString("Invalid value".to_string()),
191		};
192		invoice_error.write(&mut writer).unwrap();
193
194		let buffer = writer.0;
195		match InvoiceError::read(&mut &buffer[..]) {
196			Ok(invoice_error) => {
197				assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
198				assert_eq!(
199					invoice_error.erroneous_field,
200					Some(ErroneousField { tlv_fieldnum: 42, suggested_value: None }),
201				);
202			}
203			Err(e) => panic!("Unexpected error: {:?}", e),
204		}
205	}
206
207	#[test]
208	fn fails_parsing_invoice_error_without_message() {
209		let tlv_fieldnum: Option<u64> = None;
210		let suggested_value: Option<&Vec<u8>> = None;
211		let error: Option<&String> = None;
212
213		let mut writer = VecWriter(Vec::new());
214		let mut write_tlv = || -> Result<(), DecodeError> {
215			write_tlv_fields!(&mut writer, {
216				(1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
217				(3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
218				(5, error, (option, encoding: (String, WithoutLength))),
219			});
220			Ok(())
221		};
222		write_tlv().unwrap();
223
224		let buffer = writer.0;
225		match InvoiceError::read(&mut &buffer[..]) {
226			Ok(_) => panic!("Expected error"),
227			Err(e) => {
228				assert_eq!(e, DecodeError::InvalidValue);
229			},
230		}
231	}
232
233	#[test]
234	fn fails_parsing_invoice_error_without_field() {
235		let tlv_fieldnum: Option<u64> = None;
236		let suggested_value = vec![42; 32];
237		let error = "Invalid value".to_string();
238
239		let mut writer = VecWriter(Vec::new());
240		let mut write_tlv = || -> Result<(), DecodeError> {
241			write_tlv_fields!(&mut writer, {
242				(1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
243				(3, Some(&suggested_value), (option, encoding: (Vec<u8>, WithoutLength))),
244				(5, Some(&error), (option, encoding: (String, WithoutLength))),
245			});
246			Ok(())
247		};
248		write_tlv().unwrap();
249
250		let buffer = writer.0;
251		match InvoiceError::read(&mut &buffer[..]) {
252			Ok(_) => panic!("Expected error"),
253			Err(e) => {
254				assert_eq!(e, DecodeError::InvalidValue);
255			},
256		}
257	}
258}