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::types::string::UntrustedString;
17use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
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 { erroneous_field: None, message: UntrustedString(s) }
57	}
58}
59
60impl core::fmt::Display for InvoiceError {
61	fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
62		self.message.fmt(f)
63	}
64}
65
66impl Writeable for InvoiceError {
67	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
68		let tlv_fieldnum = self.erroneous_field.as_ref().map(|f| f.tlv_fieldnum);
69		let suggested_value =
70			self.erroneous_field.as_ref().and_then(|f| f.suggested_value.as_ref());
71		write_tlv_fields!(writer, {
72			(1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
73			(3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
74			(5, WithoutLength(&self.message), required),
75		});
76		Ok(())
77	}
78}
79
80impl Readable for InvoiceError {
81	fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
82		_init_and_read_len_prefixed_tlv_fields!(reader, {
83			(1, erroneous_field, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
84			(3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
85			(5, error, (option, encoding: (UntrustedString, WithoutLength))),
86		});
87
88		let erroneous_field = match (erroneous_field, suggested_value) {
89			(None, None) => None,
90			(None, Some(_)) => return Err(DecodeError::InvalidValue),
91			(Some(tlv_fieldnum), suggested_value) => {
92				Some(ErroneousField { tlv_fieldnum, suggested_value })
93			},
94		};
95
96		let message = match error {
97			None => return Err(DecodeError::InvalidValue),
98			Some(error) => error,
99		};
100
101		Ok(InvoiceError { erroneous_field, message })
102	}
103}
104
105impl From<Bolt12SemanticError> for InvoiceError {
106	fn from(error: Bolt12SemanticError) -> Self {
107		InvoiceError { erroneous_field: None, message: UntrustedString(format!("{:?}", error)) }
108	}
109}
110
111impl From<SignError> for InvoiceError {
112	fn from(error: SignError) -> Self {
113		let message = match error {
114			SignError::Signing => "Failed signing invoice",
115			SignError::Verification(_) => "Failed invoice signature verification",
116		};
117		InvoiceError { erroneous_field: None, message: UntrustedString(message.to_string()) }
118	}
119}
120
121#[cfg(test)]
122mod tests {
123	use super::{ErroneousField, InvoiceError};
124
125	use crate::ln::msgs::DecodeError;
126	use crate::types::string::UntrustedString;
127	use crate::util::ser::{
128		HighZeroBytesDroppedBigSize, Readable, VecWriter, WithoutLength, Writeable,
129	};
130
131	#[test]
132	fn parses_invoice_error_without_erroneous_field() {
133		let mut writer = VecWriter(Vec::new());
134		let invoice_error = InvoiceError {
135			erroneous_field: None,
136			message: UntrustedString("Invalid value".to_string()),
137		};
138		invoice_error.write(&mut writer).unwrap();
139
140		let buffer = writer.0;
141		match InvoiceError::read(&mut &buffer[..]) {
142			Ok(invoice_error) => {
143				assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
144				assert_eq!(invoice_error.erroneous_field, None);
145			},
146			Err(e) => panic!("Unexpected error: {:?}", e),
147		}
148	}
149
150	#[test]
151	fn parses_invoice_error_with_erroneous_field() {
152		let mut writer = VecWriter(Vec::new());
153		let invoice_error = InvoiceError {
154			erroneous_field: Some(ErroneousField {
155				tlv_fieldnum: 42,
156				suggested_value: Some(vec![42; 32]),
157			}),
158			message: UntrustedString("Invalid value".to_string()),
159		};
160		invoice_error.write(&mut writer).unwrap();
161
162		let buffer = writer.0;
163		match InvoiceError::read(&mut &buffer[..]) {
164			Ok(invoice_error) => {
165				assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
166				assert_eq!(
167					invoice_error.erroneous_field,
168					Some(ErroneousField { tlv_fieldnum: 42, suggested_value: Some(vec![42; 32]) }),
169				);
170			},
171			Err(e) => panic!("Unexpected error: {:?}", e),
172		}
173	}
174
175	#[test]
176	fn parses_invoice_error_without_suggested_value() {
177		let mut writer = VecWriter(Vec::new());
178		let invoice_error = InvoiceError {
179			erroneous_field: Some(ErroneousField { tlv_fieldnum: 42, suggested_value: None }),
180			message: UntrustedString("Invalid value".to_string()),
181		};
182		invoice_error.write(&mut writer).unwrap();
183
184		let buffer = writer.0;
185		match InvoiceError::read(&mut &buffer[..]) {
186			Ok(invoice_error) => {
187				assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
188				assert_eq!(
189					invoice_error.erroneous_field,
190					Some(ErroneousField { tlv_fieldnum: 42, suggested_value: None }),
191				);
192			},
193			Err(e) => panic!("Unexpected error: {:?}", e),
194		}
195	}
196
197	#[test]
198	fn fails_parsing_invoice_error_without_message() {
199		let tlv_fieldnum: Option<u64> = None;
200		let suggested_value: Option<&Vec<u8>> = None;
201		let error: Option<&String> = None;
202
203		let mut writer = VecWriter(Vec::new());
204		let mut write_tlv = || -> Result<(), DecodeError> {
205			write_tlv_fields!(&mut writer, {
206				(1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
207				(3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
208				(5, error, (option, encoding: (String, WithoutLength))),
209			});
210			Ok(())
211		};
212		write_tlv().unwrap();
213
214		let buffer = writer.0;
215		match InvoiceError::read(&mut &buffer[..]) {
216			Ok(_) => panic!("Expected error"),
217			Err(e) => {
218				assert_eq!(e, DecodeError::InvalidValue);
219			},
220		}
221	}
222
223	#[test]
224	fn fails_parsing_invoice_error_without_field() {
225		let tlv_fieldnum: Option<u64> = None;
226		let suggested_value = vec![42; 32];
227		let error = "Invalid value".to_string();
228
229		let mut writer = VecWriter(Vec::new());
230		let mut write_tlv = || -> Result<(), DecodeError> {
231			write_tlv_fields!(&mut writer, {
232				(1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
233				(3, Some(&suggested_value), (option, encoding: (Vec<u8>, WithoutLength))),
234				(5, Some(&error), (option, encoding: (String, WithoutLength))),
235			});
236			Ok(())
237		};
238		write_tlv().unwrap();
239
240		let buffer = writer.0;
241		match InvoiceError::read(&mut &buffer[..]) {
242			Ok(_) => panic!("Expected error"),
243			Err(e) => {
244				assert_eq!(e, DecodeError::InvalidValue);
245			},
246		}
247	}
248}