]> git.bitcoin.ninja Git - rust-lightning/blob - lightning/src/offers/parse.rs
Merge pull request #1908 from jkczyz/2022-11-refund
[rust-lightning] / lightning / src / offers / parse.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 //! Parsing and formatting for bech32 message encoding.
11
12 use bitcoin::bech32;
13 use bitcoin::bech32::{FromBase32, ToBase32};
14 use bitcoin::secp256k1;
15 use core::convert::TryFrom;
16 use core::fmt;
17 use crate::io;
18 use crate::ln::msgs::DecodeError;
19 use crate::util::ser::SeekReadable;
20
21 use crate::prelude::*;
22
23 /// Indicates a message can be encoded using bech32.
24 pub(super) trait Bech32Encode: AsRef<[u8]> + TryFrom<Vec<u8>, Error=ParseError> {
25         /// Human readable part of the message's bech32 encoding.
26         const BECH32_HRP: &'static str;
27
28         /// Parses a bech32-encoded message into a TLV stream.
29         fn from_bech32_str(s: &str) -> Result<Self, ParseError> {
30                 // Offer encoding may be split by '+' followed by optional whitespace.
31                 let encoded = match s.split('+').skip(1).next() {
32                         Some(_) => {
33                                 for chunk in s.split('+') {
34                                         let chunk = chunk.trim_start();
35                                         if chunk.is_empty() || chunk.contains(char::is_whitespace) {
36                                                 return Err(ParseError::InvalidContinuation);
37                                         }
38                                 }
39
40                                 let s = s.chars().filter(|c| *c != '+' && !c.is_whitespace()).collect::<String>();
41                                 Bech32String::Owned(s)
42                         },
43                         None => Bech32String::Borrowed(s),
44                 };
45
46                 let (hrp, data) = bech32::decode_without_checksum(encoded.as_ref())?;
47
48                 if hrp != Self::BECH32_HRP {
49                         return Err(ParseError::InvalidBech32Hrp);
50                 }
51
52                 let data = Vec::<u8>::from_base32(&data)?;
53                 Self::try_from(data)
54         }
55
56         /// Formats the message using bech32-encoding.
57         fn fmt_bech32_str(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
58                 bech32::encode_without_checksum_to_fmt(f, Self::BECH32_HRP, self.as_ref().to_base32())
59                         .expect("HRP is invalid").unwrap();
60
61                 Ok(())
62         }
63 }
64
65 // Used to avoid copying a bech32 string not containing the continuation character (+).
66 enum Bech32String<'a> {
67         Borrowed(&'a str),
68         Owned(String),
69 }
70
71 impl<'a> AsRef<str> for Bech32String<'a> {
72         fn as_ref(&self) -> &str {
73                 match self {
74                         Bech32String::Borrowed(s) => s,
75                         Bech32String::Owned(s) => s,
76                 }
77         }
78 }
79
80 /// A wrapper for reading a message as a TLV stream `T` from a byte sequence, while still
81 /// maintaining ownership of the bytes for later use.
82 pub(super) struct ParsedMessage<T: SeekReadable> {
83         pub bytes: Vec<u8>,
84         pub tlv_stream: T,
85 }
86
87 impl<T: SeekReadable> TryFrom<Vec<u8>> for ParsedMessage<T> {
88         type Error = DecodeError;
89
90         fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
91                 let mut cursor = io::Cursor::new(bytes);
92                 let tlv_stream: T = SeekReadable::read(&mut cursor)?;
93
94                 // Ensure that there are no more TLV records left to parse.
95                 if cursor.position() < cursor.get_ref().len() as u64 {
96                         return Err(DecodeError::InvalidValue);
97                 }
98
99                 let bytes = cursor.into_inner();
100                 Ok(Self { bytes, tlv_stream })
101         }
102 }
103
104 /// Error when parsing a bech32 encoded message using [`str::parse`].
105 #[derive(Debug, PartialEq)]
106 pub enum ParseError {
107         /// The bech32 encoding does not conform to the BOLT 12 requirements for continuing messages
108         /// across multiple parts (i.e., '+' followed by whitespace).
109         InvalidContinuation,
110         /// The bech32 encoding's human-readable part does not match what was expected for the message
111         /// being parsed.
112         InvalidBech32Hrp,
113         /// The string could not be bech32 decoded.
114         Bech32(bech32::Error),
115         /// The bech32 decoded string could not be decoded as the expected message type.
116         Decode(DecodeError),
117         /// The parsed message has invalid semantics.
118         InvalidSemantics(SemanticError),
119         /// The parsed message has an invalid signature.
120         InvalidSignature(secp256k1::Error),
121 }
122
123 /// Error when interpreting a TLV stream as a specific type.
124 #[derive(Debug, PartialEq)]
125 pub enum SemanticError {
126         /// The current [`std::time::SystemTime`] is past the offer or invoice's expiration.
127         AlreadyExpired,
128         /// The provided chain hash does not correspond to a supported chain.
129         UnsupportedChain,
130         /// A chain was provided but was not expected.
131         UnexpectedChain,
132         /// An amount was expected but was missing.
133         MissingAmount,
134         /// The amount exceeded the total bitcoin supply.
135         InvalidAmount,
136         /// An amount was provided but was not sufficient in value.
137         InsufficientAmount,
138         /// An amount was provided but was not expected.
139         UnexpectedAmount,
140         /// A currency was provided that is not supported.
141         UnsupportedCurrency,
142         /// A feature was required but is unknown.
143         UnknownRequiredFeatures,
144         /// Features were provided but were not expected.
145         UnexpectedFeatures,
146         /// A required description was not provided.
147         MissingDescription,
148         /// A signing pubkey was not provided.
149         MissingSigningPubkey,
150         /// A signing pubkey was provided but was not expected.
151         UnexpectedSigningPubkey,
152         /// A quantity was expected but was missing.
153         MissingQuantity,
154         /// An unsupported quantity was provided.
155         InvalidQuantity,
156         /// A quantity or quantity bounds was provided but was not expected.
157         UnexpectedQuantity,
158         /// Payer metadata was expected but was missing.
159         MissingPayerMetadata,
160         /// A payer id was expected but was missing.
161         MissingPayerId,
162 }
163
164 impl From<bech32::Error> for ParseError {
165         fn from(error: bech32::Error) -> Self {
166                 Self::Bech32(error)
167         }
168 }
169
170 impl From<DecodeError> for ParseError {
171         fn from(error: DecodeError) -> Self {
172                 Self::Decode(error)
173         }
174 }
175
176 impl From<SemanticError> for ParseError {
177         fn from(error: SemanticError) -> Self {
178                 Self::InvalidSemantics(error)
179         }
180 }
181
182 impl From<secp256k1::Error> for ParseError {
183         fn from(error: secp256k1::Error) -> Self {
184                 Self::InvalidSignature(error)
185         }
186 }