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