1#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
44use alloc::{string::String, vec::Vec};
45use core::fmt;
46
47use crate::error::write_err;
48use crate::primitives::decode::SegwitCodeLengthError;
49#[cfg(feature = "alloc")]
50use crate::primitives::decode::{SegwitHrpstring, SegwitHrpstringError};
51use crate::primitives::gf32::Fe32;
52use crate::primitives::hrp::Hrp;
53use crate::primitives::iter::{ByteIterExt, Fe32IterExt};
54#[cfg(feature = "alloc")]
55use crate::primitives::segwit;
56use crate::primitives::segwit::{
57 InvalidWitnessVersionError, WitnessLengthError, MAX_STRING_LENGTH,
58};
59use crate::primitives::{Bech32, Bech32m};
60
61#[rustfmt::skip] #[doc(inline)]
63pub use {
64 crate::primitives::segwit::{VERSION_0, VERSION_1},
65};
66
67#[cfg(feature = "alloc")]
81#[inline]
82pub fn decode(s: &str) -> Result<(Hrp, Fe32, Vec<u8>), DecodeError> {
83 let segwit = SegwitHrpstring::new(s)?;
84 Ok((segwit.hrp(), segwit.witness_version(), segwit.byte_iter().collect::<Vec<u8>>()))
85}
86
87#[cfg(feature = "alloc")]
103#[inline]
104pub fn encode(
105 hrp: Hrp,
106 witness_version: Fe32,
107 witness_program: &[u8],
108) -> Result<String, EncodeError> {
109 segwit::validate_witness_version(witness_version)?;
110 segwit::validate_witness_program_length(witness_program.len(), witness_version)?;
111
112 let _ = encoded_length(hrp, witness_version, witness_program)?;
113
114 let mut buf = String::new();
115 encode_to_fmt_unchecked(&mut buf, hrp, witness_version, witness_program)?;
116 Ok(buf)
117}
118
119#[cfg(feature = "alloc")]
124#[inline]
125pub fn encode_v0(hrp: Hrp, witness_program: &[u8]) -> Result<String, EncodeError> {
126 encode(hrp, VERSION_0, witness_program)
127}
128
129#[cfg(feature = "alloc")]
134#[inline]
135pub fn encode_v1(hrp: Hrp, witness_program: &[u8]) -> Result<String, EncodeError> {
136 encode(hrp, VERSION_1, witness_program)
137}
138
139#[inline]
144pub fn encode_to_fmt_unchecked<W: fmt::Write>(
145 fmt: &mut W,
146 hrp: Hrp,
147 witness_version: Fe32,
148 witness_program: &[u8],
149) -> fmt::Result {
150 encode_lower_to_fmt_unchecked(fmt, hrp, witness_version, witness_program)
151}
152
153pub fn encode_lower_to_fmt_unchecked<W: fmt::Write>(
158 fmt: &mut W,
159 hrp: Hrp,
160 witness_version: Fe32,
161 witness_program: &[u8],
162) -> fmt::Result {
163 let mut buf = [0u8; MAX_STRING_LENGTH];
164 let mut pos = 0;
165
166 let iter = witness_program.iter().copied().bytes_to_fes();
167 match witness_version {
168 VERSION_0 => {
169 let bytes = iter.with_checksum::<Bech32>(&hrp).with_witness_version(VERSION_0).bytes();
170 buf.iter_mut().zip(bytes).for_each(|(dst, src)| {
171 *dst = src;
172 pos += 1;
173 });
174 }
175 version => {
176 let bytes = iter.with_checksum::<Bech32m>(&hrp).with_witness_version(version).bytes();
177 buf.iter_mut().zip(bytes).for_each(|(dst, src)| {
178 *dst = src;
179 pos += 1;
180 });
181 }
182 }
183
184 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
185 fmt.write_str(s)?;
186
187 Ok(())
188}
189
190#[inline]
197pub fn encode_upper_to_fmt_unchecked<W: fmt::Write>(
198 fmt: &mut W,
199 hrp: Hrp,
200 witness_version: Fe32,
201 witness_program: &[u8],
202) -> fmt::Result {
203 let mut buf = [0u8; MAX_STRING_LENGTH];
204 let mut pos = 0;
205
206 let iter = witness_program.iter().copied().bytes_to_fes();
207 match witness_version {
208 VERSION_0 => {
209 let bytes = iter.with_checksum::<Bech32>(&hrp).with_witness_version(VERSION_0).bytes();
210 buf.iter_mut().zip(bytes).for_each(|(dst, src)| {
211 *dst = src.to_ascii_uppercase();
212 pos += 1;
213 });
214 }
215 version => {
216 let bytes = iter.with_checksum::<Bech32m>(&hrp).with_witness_version(version).bytes();
217 buf.iter_mut().zip(bytes).for_each(|(dst, src)| {
218 *dst = src.to_ascii_uppercase();
219 pos += 1;
220 });
221 }
222 }
223
224 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
225 fmt.write_str(s)?;
226
227 Ok(())
228}
229
230#[cfg(feature = "std")]
237#[inline]
238pub fn encode_to_writer_unchecked<W: std::io::Write>(
239 w: &mut W,
240 hrp: Hrp,
241 witness_version: Fe32,
242 witness_program: &[u8],
243) -> std::io::Result<()> {
244 encode_lower_to_writer_unchecked(w, hrp, witness_version, witness_program)
245}
246
247#[cfg(feature = "std")]
254#[inline]
255pub fn encode_lower_to_writer_unchecked<W: std::io::Write>(
256 w: &mut W,
257 hrp: Hrp,
258 witness_version: Fe32,
259 witness_program: &[u8],
260) -> std::io::Result<()> {
261 let mut buf = [0u8; MAX_STRING_LENGTH];
262 let mut pos = 0;
263
264 let iter = witness_program.iter().copied().bytes_to_fes();
265 match witness_version {
266 VERSION_0 => {
267 let bytes = iter.with_checksum::<Bech32>(&hrp).with_witness_version(VERSION_0).bytes();
268 buf.iter_mut().zip(bytes).for_each(|(dst, src)| {
269 *dst = src;
270 pos += 1;
271 });
272 }
273 version => {
274 let bytes = iter.with_checksum::<Bech32m>(&hrp).with_witness_version(version).bytes();
275 buf.iter_mut().zip(bytes).for_each(|(dst, src)| {
276 *dst = src;
277 pos += 1;
278 });
279 }
280 }
281
282 w.write_all(&buf[..pos])?;
283
284 Ok(())
285}
286
287#[cfg(feature = "std")]
296#[inline]
297pub fn encode_upper_to_writer_unchecked<W: std::io::Write>(
298 w: &mut W,
299 hrp: Hrp,
300 witness_version: Fe32,
301 witness_program: &[u8],
302) -> std::io::Result<()> {
303 let mut buf = [0u8; MAX_STRING_LENGTH];
304 let mut pos = 0;
305
306 let iter = witness_program.iter().copied().bytes_to_fes();
307 match witness_version {
308 VERSION_0 => {
309 let bytes = iter.with_checksum::<Bech32>(&hrp).with_witness_version(VERSION_0).bytes();
310 buf.iter_mut().zip(bytes).for_each(|(dst, src)| {
311 *dst = src.to_ascii_uppercase();
312 pos += 1;
313 });
314 }
315 version => {
316 let bytes = iter.with_checksum::<Bech32m>(&hrp).with_witness_version(version).bytes();
317 buf.iter_mut().zip(bytes).for_each(|(dst, src)| {
318 *dst = src.to_ascii_uppercase();
319 pos += 1;
320 });
321 }
322 }
323
324 w.write_all(&buf[..pos])?;
325
326 Ok(())
327}
328
329pub fn encoded_length(
336 hrp: Hrp,
337 _witness_version: Fe32, witness_program: &[u8],
339) -> Result<usize, SegwitCodeLengthError> {
340 let len = crate::encoded_length::<Bech32>(hrp, witness_program).map(|len| len + 1)?; if len > MAX_STRING_LENGTH {
344 Err(SegwitCodeLengthError(len))
345 } else {
346 Ok(len)
347 }
348}
349
350#[cfg(feature = "alloc")]
352#[derive(Debug, Clone, PartialEq, Eq)]
353#[non_exhaustive]
354pub struct DecodeError(pub SegwitHrpstringError);
355
356#[cfg(feature = "alloc")]
357impl fmt::Display for DecodeError {
358 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
359 write_err!(f, "decoding segwit address failed"; self.0)
360 }
361}
362
363#[cfg(feature = "std")]
364impl std::error::Error for DecodeError {
365 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
366}
367
368#[cfg(feature = "alloc")]
369impl From<SegwitHrpstringError> for DecodeError {
370 #[inline]
371 fn from(e: SegwitHrpstringError) -> Self { Self(e) }
372}
373
374#[derive(Debug, Clone, PartialEq, Eq)]
376#[non_exhaustive]
377#[cfg(feature = "alloc")]
378pub enum EncodeError {
379 WitnessVersion(InvalidWitnessVersionError),
381 WitnessLength(WitnessLengthError),
383 TooLong(SegwitCodeLengthError),
385 Fmt(fmt::Error),
387}
388
389#[cfg(feature = "alloc")]
390impl fmt::Display for EncodeError {
391 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
392 use EncodeError::*;
393
394 match *self {
395 WitnessVersion(ref e) => write_err!(f, "witness version"; e),
396 WitnessLength(ref e) => write_err!(f, "witness length"; e),
397 TooLong(ref e) => write_err!(f, "encode error"; e),
398 Fmt(ref e) => write_err!(f, "writing to formatter failed"; e),
399 }
400 }
401}
402
403#[cfg(feature = "std")]
404#[cfg(feature = "alloc")]
405impl std::error::Error for EncodeError {
406 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
407 use EncodeError::*;
408
409 match *self {
410 WitnessVersion(ref e) => Some(e),
411 WitnessLength(ref e) => Some(e),
412 TooLong(ref e) => Some(e),
413 Fmt(ref e) => Some(e),
414 }
415 }
416}
417
418#[cfg(feature = "alloc")]
419impl From<InvalidWitnessVersionError> for EncodeError {
420 #[inline]
421 fn from(e: InvalidWitnessVersionError) -> Self { Self::WitnessVersion(e) }
422}
423
424#[cfg(feature = "alloc")]
425impl From<WitnessLengthError> for EncodeError {
426 #[inline]
427 fn from(e: WitnessLengthError) -> Self { Self::WitnessLength(e) }
428}
429
430#[cfg(feature = "alloc")]
431impl From<SegwitCodeLengthError> for EncodeError {
432 #[inline]
433 fn from(e: SegwitCodeLengthError) -> Self { Self::TooLong(e) }
434}
435
436#[cfg(feature = "alloc")]
437impl From<fmt::Error> for EncodeError {
438 #[inline]
439 fn from(e: fmt::Error) -> Self { Self::Fmt(e) }
440}
441
442#[cfg(all(test, feature = "alloc"))]
443mod tests {
444 use super::*;
445 use crate::primitives::decode::{SegwitCodeLengthError, SegwitHrpstringError};
446 use crate::primitives::hrp;
447
448 #[test]
449 fn roundtrip_valid_mainnet_addresses() {
452 let addresses = vec![
454 "bc1q2s3rjwvam9dt2ftt4sqxqjf3twav0gdx0k0q2etxflx38c3x8tnssdmnjq", "bc1py3m7vwnghyne9gnvcjw82j7gqt2rafgdmlmwmqnn3hvcmdm09rjqcgrtxs", ];
457
458 for address in addresses {
459 let (hrp, version, program) = decode(address).expect("failed to decode valid address");
460 let encoded = encode(hrp, version, &program).expect("failed to encode address");
461 assert_eq!(encoded, address);
462 }
463 }
464
465 fn witness_program() -> [u8; 20] {
466 [
467 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3,
468 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
469 ]
470 }
471
472 #[test]
473 fn encode_lower_to_fmt() {
474 let program = witness_program();
475 let mut address = String::new();
476 encode_to_fmt_unchecked(&mut address, hrp::BC, VERSION_0, &program)
477 .expect("failed to encode address to QR code");
478
479 let want = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4";
480 assert_eq!(address, want);
481 }
482
483 #[test]
484 fn encode_upper_to_fmt() {
485 let program = witness_program();
486 let mut address = String::new();
487 encode_upper_to_fmt_unchecked(&mut address, hrp::BC, VERSION_0, &program)
488 .expect("failed to encode address to QR code");
489
490 let want = "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4";
491 assert_eq!(address, want);
492 }
493
494 #[test]
495 #[cfg(feature = "std")]
496 fn encode_lower_to_writer() {
497 let program = witness_program();
498 let mut buf = Vec::new();
499 encode_lower_to_writer_unchecked(&mut buf, hrp::BC, VERSION_0, &program)
500 .expect("failed to encode");
501
502 let address = std::str::from_utf8(&buf).expect("ascii is valid utf8");
503 let want = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4";
504 assert_eq!(address, want);
505 }
506
507 #[test]
508 #[cfg(feature = "std")]
509 fn encode_upper_to_writer() {
510 let program = witness_program();
511 let mut buf = Vec::new();
512 encode_upper_to_writer_unchecked(&mut buf, hrp::BC, VERSION_0, &program)
513 .expect("failed to encode");
514
515 let address = std::str::from_utf8(&buf).expect("ascii is valid utf8");
516 let want = "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4";
517 assert_eq!(address, want);
518 }
519
520 #[test]
521 #[cfg(feature = "std")]
522 fn encode_lower_to_writer_including_lowecaseing_hrp() {
523 let program = witness_program();
524 let mut buf = Vec::new();
525 let hrp = Hrp::parse_unchecked("BC");
526 encode_lower_to_writer_unchecked(&mut buf, hrp, VERSION_0, &program)
527 .expect("failed to encode");
528
529 let address = std::str::from_utf8(&buf).expect("ascii is valid utf8");
530 let want = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4";
531 assert_eq!(address, want);
532 }
533
534 #[test]
535 fn encoded_length_works() {
536 let addresses = vec![
537 "bc1q2s3rjwvam9dt2ftt4sqxqjf3twav0gdx0k0q2etxflx38c3x8tnssdmnjq",
538 "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
539 ];
540
541 for address in addresses {
542 let (hrp, version, program) = decode(address).expect("valid address");
543
544 let encoded = encode(hrp, version, &program).expect("valid data");
545 let want = encoded.len();
546 let got = encoded_length(hrp, version, &program).expect("encoded length");
547
548 assert_eq!(got, want);
549 }
550 }
551
552 #[test]
553 fn can_encode_maximum_length_address() {
554 let program = [0_u8; 40]; let hrp = Hrp::parse_unchecked("anhrpthatis18chars");
556 let addr = encode(hrp, VERSION_1, &program).expect("valid data");
557 assert_eq!(addr.len(), MAX_STRING_LENGTH);
558 }
559
560 #[test]
561 fn can_not_encode_address_too_long() {
562 let tcs = vec![
563 ("anhrpthatis19charsx", 91),
564 ("anhrpthatisthemaximumallowedlengthofeightythreebytesxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 155)
565 ];
566
567 for (hrp, len) in tcs {
568 let program = [0_u8; 40]; let hrp = Hrp::parse_unchecked(hrp);
570 let err = encode(hrp, VERSION_1, &program).unwrap_err();
571 assert_eq!(err, EncodeError::TooLong(SegwitCodeLengthError(len)));
572 }
573 }
574
575 #[test]
576 fn can_decode_maximum_length_address() {
577 let address = "anhrpthatisnineteen1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqghfyyfz";
578 assert_eq!(address.len(), MAX_STRING_LENGTH);
579
580 assert!(decode(address).is_ok());
581 }
582
583 #[test]
584 fn can_not_decode_address_too_long() {
585 let address = "anhrpthatistwentycha1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqfrwjz";
586 assert_eq!(address.len(), MAX_STRING_LENGTH + 1);
587
588 assert_eq!(decode(address).unwrap_err(), DecodeError(SegwitHrpstringError::TooLong(91)));
589 }
590}