Merge pull request #2294 from jkczyz/2023-05-onion-message-replies
[rust-lightning] / lightning / src / 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
12 use crate::io;
13 use crate::ln::msgs::DecodeError;
14 use crate::offers::parse::SemanticError;
15 use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
16 use crate::util::string::UntrustedString;
17
18 use crate::prelude::*;
19
20 /// An error in response to an [`InvoiceRequest`] or an [`Invoice`].
21 ///
22 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
23 /// [`Invoice`]: crate::offers::invoice::Invoice
24 #[derive(Clone, Debug)]
25 #[cfg_attr(test, derive(PartialEq))]
26 pub struct InvoiceError {
27         /// The field in the [`InvoiceRequest`] or the [`Invoice`] that contained an error.
28         ///
29         /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
30         /// [`Invoice`]: crate::offers::invoice::Invoice
31         pub erroneous_field: Option<ErroneousField>,
32
33         /// An explanation of the error.
34         pub message: UntrustedString,
35 }
36
37 /// The field in the [`InvoiceRequest`] or the [`Invoice`] that contained an error.
38 ///
39 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
40 /// [`Invoice`]: crate::offers::invoice::Invoice
41 #[derive(Clone, Debug)]
42 #[cfg_attr(test, derive(PartialEq))]
43 pub struct ErroneousField {
44         /// The type number of the TLV field containing the error.
45         pub tlv_fieldnum: u64,
46
47         /// A value to use for the TLV field to avoid the error.
48         pub suggested_value: Option<Vec<u8>>,
49 }
50
51 impl core::fmt::Display for InvoiceError {
52         fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
53                 self.message.fmt(f)
54         }
55 }
56
57 impl Writeable for InvoiceError {
58         fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
59                 let tlv_fieldnum = self.erroneous_field.as_ref().map(|f| f.tlv_fieldnum);
60                 let suggested_value =
61                         self.erroneous_field.as_ref().and_then(|f| f.suggested_value.as_ref());
62                 write_tlv_fields!(writer, {
63                         (1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
64                         (3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
65                         (5, WithoutLength(&self.message), required),
66                 });
67                 Ok(())
68         }
69 }
70
71 impl Readable for InvoiceError {
72         fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
73                 _init_and_read_tlv_fields!(reader, {
74                         (1, erroneous_field, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
75                         (3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
76                         (5, error, (option, encoding: (UntrustedString, WithoutLength))),
77                 });
78
79                 let erroneous_field = match (erroneous_field, suggested_value) {
80                         (None, None) => None,
81                         (None, Some(_)) => return Err(DecodeError::InvalidValue),
82                         (Some(tlv_fieldnum), suggested_value) => {
83                                 Some(ErroneousField { tlv_fieldnum, suggested_value })
84                         },
85                 };
86
87                 let message = match error {
88                         None => return Err(DecodeError::InvalidValue),
89                         Some(error) => error,
90                 };
91
92                 Ok(InvoiceError { erroneous_field, message })
93         }
94 }
95
96 impl From<SemanticError> for InvoiceError {
97         fn from(error: SemanticError) -> Self {
98                 InvoiceError {
99                         erroneous_field: None,
100                         message: UntrustedString(format!("{:?}", error)),
101                 }
102         }
103 }
104
105 #[cfg(test)]
106 mod tests {
107         use super::{ErroneousField, InvoiceError};
108
109         use crate::ln::msgs::DecodeError;
110         use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, VecWriter, WithoutLength, Writeable};
111         use crate::util::string::UntrustedString;
112
113         #[test]
114         fn parses_invoice_error_without_erroneous_field() {
115                 let mut writer = VecWriter(Vec::new());
116                 let invoice_error = InvoiceError {
117                         erroneous_field: None,
118                         message: UntrustedString("Invalid value".to_string()),
119                 };
120                 invoice_error.write(&mut writer).unwrap();
121
122                 let buffer = writer.0;
123                 match InvoiceError::read(&mut &buffer[..]) {
124                         Ok(invoice_error) => {
125                                 assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
126                                 assert_eq!(invoice_error.erroneous_field, None);
127                         }
128                         Err(e) => panic!("Unexpected error: {:?}", e),
129                 }
130         }
131
132         #[test]
133         fn parses_invoice_error_with_erroneous_field() {
134                 let mut writer = VecWriter(Vec::new());
135                 let invoice_error = InvoiceError {
136                         erroneous_field: Some(ErroneousField {
137                                 tlv_fieldnum: 42,
138                                 suggested_value: Some(vec![42; 32]),
139                         }),
140                         message: UntrustedString("Invalid value".to_string()),
141                 };
142                 invoice_error.write(&mut writer).unwrap();
143
144                 let buffer = writer.0;
145                 match InvoiceError::read(&mut &buffer[..]) {
146                         Ok(invoice_error) => {
147                                 assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
148                                 assert_eq!(
149                                         invoice_error.erroneous_field,
150                                         Some(ErroneousField { tlv_fieldnum: 42, suggested_value: Some(vec![42; 32]) }),
151                                 );
152                         }
153                         Err(e) => panic!("Unexpected error: {:?}", e),
154                 }
155         }
156
157         #[test]
158         fn parses_invoice_error_without_suggested_value() {
159                 let mut writer = VecWriter(Vec::new());
160                 let invoice_error = InvoiceError {
161                         erroneous_field: Some(ErroneousField {
162                                 tlv_fieldnum: 42,
163                                 suggested_value: None,
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: None }),
176                                 );
177                         }
178                         Err(e) => panic!("Unexpected error: {:?}", e),
179                 }
180         }
181
182         #[test]
183         fn fails_parsing_invoice_error_without_message() {
184                 let tlv_fieldnum: Option<u64> = None;
185                 let suggested_value: Option<&Vec<u8>> = None;
186                 let error: Option<&String> = None;
187
188                 let mut writer = VecWriter(Vec::new());
189                 let mut write_tlv = || -> Result<(), DecodeError> {
190                         write_tlv_fields!(&mut writer, {
191                                 (1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
192                                 (3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
193                                 (5, error, (option, encoding: (String, WithoutLength))),
194                         });
195                         Ok(())
196                 };
197                 write_tlv().unwrap();
198
199                 let buffer = writer.0;
200                 match InvoiceError::read(&mut &buffer[..]) {
201                         Ok(_) => panic!("Expected error"),
202                         Err(e) => {
203                                 assert_eq!(e, DecodeError::InvalidValue);
204                         },
205                 }
206         }
207
208         #[test]
209         fn fails_parsing_invoice_error_without_field() {
210                 let tlv_fieldnum: Option<u64> = None;
211                 let suggested_value = vec![42; 32];
212                 let error = "Invalid value".to_string();
213
214                 let mut writer = VecWriter(Vec::new());
215                 let mut write_tlv = || -> Result<(), DecodeError> {
216                         write_tlv_fields!(&mut writer, {
217                                 (1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
218                                 (3, Some(&suggested_value), (option, encoding: (Vec<u8>, WithoutLength))),
219                                 (5, Some(&error), (option, encoding: (String, WithoutLength))),
220                         });
221                         Ok(())
222                 };
223                 write_tlv().unwrap();
224
225                 let buffer = writer.0;
226                 match InvoiceError::read(&mut &buffer[..]) {
227                         Ok(_) => panic!("Expected error"),
228                         Err(e) => {
229                                 assert_eq!(e, DecodeError::InvalidValue);
230                         },
231                 }
232         }
233 }