| 
 | 1 | +// SPDX-License-Identifier: MIT  | 
 | 2 | + | 
 | 3 | +//! Segregated Witness API - enabling typical usage for encoding and decoding segwit addresses.  | 
 | 4 | +//!  | 
 | 5 | +//! The bips contain some complexity, this module should allow you to create modern bitcoin  | 
 | 6 | +//! addresses, and parse existing addresses from the Bitcoin blockchain, correctly and easily.  | 
 | 7 | +//!  | 
 | 8 | +//! You should not need to intimately know [BIP-173] and [BIP-350] in order to correctly use this  | 
 | 9 | +//! module. If you are doing unusual things, consider using the `primitives` submodules directly.  | 
 | 10 | +//!  | 
 | 11 | +//! Note, we do implement the bips to spec, however this is done in the `primitives` submodules, to  | 
 | 12 | +//! convince yourself, and to see the nitty gritty, you can look at the test vector code in  | 
 | 13 | +//! [`bip_173_test_vectors.rs`] and [`bip_350_test_vectors.rs`].  | 
 | 14 | +//!  | 
 | 15 | +//! [BIP-173]: <https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki>  | 
 | 16 | +//! [BIP-350]: <https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki>  | 
 | 17 | +//! [`bip_173_test_vectors.rs`]: <https://github.com/rust-bitcoin/rust-bech32/blob/master/tests/bip_173_test_vectors.rs>  | 
 | 18 | +//! [`bip_350_test_vectors.rs`]: <https://github.com/rust-bitcoin/rust-bech32/blob/master/tests/bip_350_test_vectors.rs>  | 
 | 19 | +
  | 
 | 20 | +use core::fmt;  | 
 | 21 | + | 
 | 22 | +use crate::primitives::decode::{SegwitHrpstring, SegwitHrpstringError};  | 
 | 23 | +use crate::primitives::gf32::Fe32;  | 
 | 24 | +// TODO: Add ergonomic re-exports of types used in this modules public API, ether to `lib.rs` or here.  | 
 | 25 | +use crate::primitives::hrp::{self, Hrp};  | 
 | 26 | +use crate::primitives::iter::{ByteIterExt, Fe32IterExt};  | 
 | 27 | +use crate::primitives::{Bech32, Bech32m};  | 
 | 28 | + | 
 | 29 | +/// Decodes a bech32 address returning the witness version and encode data.  | 
 | 30 | +///  | 
 | 31 | +/// Handles segwit v0 and v1 addresses with the respective checksum algorithm.  | 
 | 32 | +#[cfg(feature = "alloc")]  | 
 | 33 | +pub fn decode(s: &str) -> Result<(Fe32, Vec<u8>), SegwitHrpstringError> {  | 
 | 34 | +    let segwit = SegwitHrpstring::new(s)?;  | 
 | 35 | +    let version = segwit.witness_version();  | 
 | 36 | +    let data = segwit.byte_iter().collect::<Vec<u8>>();  | 
 | 37 | +    Ok((version, data))  | 
 | 38 | +}  | 
 | 39 | + | 
 | 40 | +/// Encodes a witness program as a bech32 address.  | 
 | 41 | +///  | 
 | 42 | +/// Uses segwit v1 and the bech32m checksum algorithm in line with [BIP-350]  | 
 | 43 | +///  | 
 | 44 | +/// [BIP-350]: <https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki>  | 
 | 45 | +#[cfg(feature = "alloc")]  | 
 | 46 | +pub fn encode(program: &[u8]) -> String { encode_program_as_segwit_v1_mainnet_address(program) }  | 
 | 47 | + | 
 | 48 | +/// Encodes a witness program as a segwit version 0 address suitable for use on mainnet.  | 
 | 49 | +#[cfg(feature = "alloc")]  | 
 | 50 | +pub fn encode_program_as_segwit_v0_mainnet_address(program: &[u8]) -> String {  | 
 | 51 | +    let hrp = hrp::BC;  | 
 | 52 | +    let version = Fe32::Q;  | 
 | 53 | +    encode_program(&hrp, version, program)  | 
 | 54 | +}  | 
 | 55 | + | 
 | 56 | +/// Encodes a witness program as a segwit version 1 address suitable for use on mainnet.  | 
 | 57 | +#[cfg(feature = "alloc")]  | 
 | 58 | +pub fn encode_program_as_segwit_v1_mainnet_address(program: &[u8]) -> String {  | 
 | 59 | +    let hrp = hrp::BC;  | 
 | 60 | +    let version = Fe32::P;  | 
 | 61 | +    encode_program(&hrp, version, program)  | 
 | 62 | +}  | 
 | 63 | + | 
 | 64 | +/// Encodes a witness program as a segwit version 0 address suitable for use on testnet.  | 
 | 65 | +#[cfg(feature = "alloc")]  | 
 | 66 | +pub fn encode_program_as_segwit_v0_testnet_address(program: &[u8]) -> String {  | 
 | 67 | +    let hrp = hrp::TB;  | 
 | 68 | +    let version = Fe32::Q;  | 
 | 69 | +    encode_program(&hrp, version, program)  | 
 | 70 | +}  | 
 | 71 | + | 
 | 72 | +/// Encodes a witness program as a segwit version 1 address suitable for use on testnet.  | 
 | 73 | +#[cfg(feature = "alloc")]  | 
 | 74 | +pub fn encode_program_as_segwit_v1_testnet_address(program: &[u8]) -> String {  | 
 | 75 | +    let hrp = hrp::TB;  | 
 | 76 | +    let version = Fe32::P;  | 
 | 77 | +    encode_program(&hrp, version, program)  | 
 | 78 | +}  | 
 | 79 | + | 
 | 80 | +/// Encodes a witness program as a bech32 address string using the given `hrp` and `version`.  | 
 | 81 | +#[cfg(feature = "alloc")]  | 
 | 82 | +pub fn encode_program(hrp: &Hrp, version: Fe32, program: &[u8]) -> String {  | 
 | 83 | +    // Possibly faster to use `collect` instead of writing char by char?  | 
 | 84 | +    // (Counter argument: "Early optimization is the root of all evil.")  | 
 | 85 | +    let mut buf = String::new();  | 
 | 86 | +    encode_program_to_fmt(&mut buf, hrp, version, program).expect("TODO: Handle errors");  | 
 | 87 | +    buf  | 
 | 88 | +}  | 
 | 89 | + | 
 | 90 | +/// Encodes a witness program to a writer ([`fmt::Write`]).  | 
 | 91 | +///  | 
 | 92 | +/// Prefixes with the appropriate witness version byte and appends the appropriate checksum.  | 
 | 93 | +/// Currently no checks done on the validity of the `hrp`, `version`, or `program`.  | 
 | 94 | +pub fn encode_program_to_fmt(  | 
 | 95 | +    fmt: &mut dyn fmt::Write,  | 
 | 96 | +    hrp: &Hrp,  | 
 | 97 | +    version: Fe32,  | 
 | 98 | +    program: &[u8],  | 
 | 99 | +) -> fmt::Result {  | 
 | 100 | +    match version {  | 
 | 101 | +        Fe32::Q => {  | 
 | 102 | +            for c in program  | 
 | 103 | +                .iter()  | 
 | 104 | +                .copied()  | 
 | 105 | +                .bytes_to_fes()  | 
 | 106 | +                .with_checksum::<Bech32>(hrp)  | 
 | 107 | +                .with_witness_version(Fe32::Q)  | 
 | 108 | +                .chars()  | 
 | 109 | +            {  | 
 | 110 | +                fmt.write_char(c)?;  | 
 | 111 | +            }  | 
 | 112 | +        }  | 
 | 113 | +        witver => {  | 
 | 114 | +            for c in program  | 
 | 115 | +                .iter()  | 
 | 116 | +                .copied()  | 
 | 117 | +                .bytes_to_fes()  | 
 | 118 | +                .with_checksum::<Bech32m>(hrp)  | 
 | 119 | +                .with_witness_version(witver)  | 
 | 120 | +                .chars()  | 
 | 121 | +            {  | 
 | 122 | +                fmt.write_char(c)?;  | 
 | 123 | +            }  | 
 | 124 | +        }  | 
 | 125 | +    }  | 
 | 126 | +    Ok(())  | 
 | 127 | +}  | 
 | 128 | + | 
 | 129 | +#[cfg(all(test, feature = "alloc"))]  | 
 | 130 | +mod tests {  | 
 | 131 | +    use super::*;  | 
 | 132 | + | 
 | 133 | +    #[test]  | 
 | 134 | +    fn parse_valid_mainnet_addresses() {  | 
 | 135 | +        // A few recent addresses from mainnet (Block 801266).  | 
 | 136 | +        let addresses = vec![  | 
 | 137 | +            "bc1q2s3rjwvam9dt2ftt4sqxqjf3twav0gdx0k0q2etxflx38c3x8tnssdmnjq", // Segwit v0  | 
 | 138 | +            "bc1py3m7vwnghyne9gnvcjw82j7gqt2rafgdmlmwmqnn3hvcmdm09rjqcgrtxs", // Segwit v1  | 
 | 139 | +        ];  | 
 | 140 | + | 
 | 141 | +        for address in addresses {  | 
 | 142 | +            // Just shows we handle both v0 and v1 addresses, for complete test  | 
 | 143 | +            // coverage see primitives submodules and test vectors.  | 
 | 144 | +            assert!(decode(address).is_ok());  | 
 | 145 | +        }  | 
 | 146 | +    }  | 
 | 147 | + | 
 | 148 | +    #[test]  | 
 | 149 | +    fn roundtrip_valid_v0_mainnet_address() {  | 
 | 150 | +        let address = "bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej";  | 
 | 151 | +        let (_version, data) = decode(address).expect("failed to decode address");  | 
 | 152 | +        let encoded = encode_program_as_segwit_v0_mainnet_address(&data);  | 
 | 153 | +        assert_eq!(encoded, address);  | 
 | 154 | +    }  | 
 | 155 | + | 
 | 156 | +    #[test]  | 
 | 157 | +    fn roundtrip_valid_v1_mainnet_address() {  | 
 | 158 | +        let address = "bc1pguzvu9c7d6qc9pwgu93vqmwzsr6e3kpxewjk93wkprn4suhz8w3qp3yxsd";  | 
 | 159 | +        let (_version, data) = decode(address).expect("failed to decode address");  | 
 | 160 | + | 
 | 161 | +        let explicit = encode_program_as_segwit_v1_mainnet_address(&data);  | 
 | 162 | +        let implicit = encode(&data);  | 
 | 163 | + | 
 | 164 | +        // Ensure `encode` implicitly uses v1.  | 
 | 165 | +        assert_eq!(implicit, explicit);  | 
 | 166 | + | 
 | 167 | +        // Ensure it roundtrips.  | 
 | 168 | +        assert_eq!(implicit, address);  | 
 | 169 | +    }  | 
 | 170 | +}  | 
0 commit comments