@@ -159,3 +159,94 @@ impl OnionMessageContents for DNSResolverMessage {
159
159
}
160
160
}
161
161
}
162
+
163
+ /// A struct containing the two parts of a BIP 353 Human Readable Name - the user and domain parts.
164
+ ///
165
+ /// The `user` and `domain` parts, together, cannot exceed 232 bytes in length, and both must be
166
+ /// non-empty.
167
+ ///
168
+ /// To protect against [Homograph Attacks], both parts of a Human Readable Name must be plain
169
+ /// ASCII.
170
+ ///
171
+ /// [Homograph Attacks]: https://en.wikipedia.org/wiki/IDN_homograph_attack
172
+ #[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
173
+ pub struct HumanReadableName {
174
+ // TODO Remove the heap allocations given the whole data can't be more than 256 bytes.
175
+ user : String ,
176
+ domain : String ,
177
+ }
178
+
179
+ impl HumanReadableName {
180
+ /// Constructs a new [`HumanReadableName`] from the `user` and `domain` parts. See the
181
+ /// struct-level documentation for more on the requirements on each.
182
+ pub fn new ( user : String , domain : String ) -> Result < HumanReadableName , ( ) > {
183
+ const REQUIRED_EXTRA_LEN : usize = ".user._bitcoin-payment." . len ( ) + 1 ;
184
+ if user. len ( ) + domain. len ( ) + REQUIRED_EXTRA_LEN > 255 {
185
+ return Err ( ( ) ) ;
186
+ }
187
+ if user. is_empty ( ) || domain. is_empty ( ) {
188
+ return Err ( ( ) ) ;
189
+ }
190
+ if !user. is_ascii ( ) || !domain. is_ascii ( ) {
191
+ return Err ( ( ) ) ;
192
+ }
193
+ Ok ( HumanReadableName { user, domain } )
194
+ }
195
+
196
+ /// Constructs a new [`HumanReadableName`] from the standard encoding - `user`@`domain`.
197
+ ///
198
+ /// If `user` includes the standard BIP 353 ₿ prefix it is automatically removed as required by
199
+ /// BIP 353.
200
+ pub fn from_encoded ( encoded : & str ) -> Result < HumanReadableName , ( ) > {
201
+ if let Some ( ( user, domain) ) = encoded. strip_prefix ( '₿' ) . unwrap_or ( encoded) . split_once ( "@" )
202
+ {
203
+ Self :: new ( user. to_string ( ) , domain. to_string ( ) )
204
+ } else {
205
+ Err ( ( ) )
206
+ }
207
+ }
208
+
209
+ /// Gets the `user` part of this Human Readable Name
210
+ pub fn user ( & self ) -> & str {
211
+ & self . user
212
+ }
213
+
214
+ /// Gets the `domain` part of this Human Readable Name
215
+ pub fn domain ( & self ) -> & str {
216
+ & self . domain
217
+ }
218
+ }
219
+
220
+ // Serialized per the requirements for inclusion in a BOLT 12 `invoice_request`
221
+ impl Writeable for HumanReadableName {
222
+ fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
223
+ ( self . user . len ( ) as u8 ) . write ( writer) ?;
224
+ writer. write_all ( & self . user . as_bytes ( ) ) ?;
225
+ ( self . domain . len ( ) as u8 ) . write ( writer) ?;
226
+ writer. write_all ( & self . domain . as_bytes ( ) )
227
+ }
228
+ }
229
+
230
+ impl Readable for HumanReadableName {
231
+ fn read < R : io:: Read > ( reader : & mut R ) -> Result < Self , DecodeError > {
232
+ let mut read_bytes = [ 0 ; 255 ] ;
233
+
234
+ let user_len: u8 = Readable :: read ( reader) ?;
235
+ reader. read_exact ( & mut read_bytes[ ..user_len as usize ] ) ?;
236
+ let user_bytes: Vec < u8 > = read_bytes[ ..user_len as usize ] . into ( ) ;
237
+ let user = match String :: from_utf8 ( user_bytes) {
238
+ Ok ( user) => user,
239
+ Err ( _) => return Err ( DecodeError :: InvalidValue ) ,
240
+ } ;
241
+
242
+ let domain_len: u8 = Readable :: read ( reader) ?;
243
+ reader. read_exact ( & mut read_bytes[ ..domain_len as usize ] ) ?;
244
+ let domain_bytes: Vec < u8 > = read_bytes[ ..domain_len as usize ] . into ( ) ;
245
+ let domain = match String :: from_utf8 ( domain_bytes) {
246
+ Ok ( domain) => domain,
247
+ Err ( _) => return Err ( DecodeError :: InvalidValue ) ,
248
+ } ;
249
+
250
+ HumanReadableName :: new ( user, domain) . map_err ( |( ) | DecodeError :: InvalidValue )
251
+ }
252
+ }
0 commit comments