Merge rust-bitcoin/rust-bitcoin#1951: Use newly released bech32 API

e4c7e01a6f Use the new bech32 iterator API (Tobin C. Harding)

Pull request description:

  Depend on the newly released version of `bech32`, BOOM!

ACKs for top commit:
  apoelstra:
    ACK e4c7e01a6f
  clarkmoody:
    ACK e4c7e01a6f

Tree-SHA512: 91675a830cf67f8dcabd42e7dc1b70d80b669330be5244bb8102e0ec5d1a206d5ead07f73b328a158b761c328bc78d573185af8d31f14183ccc17318d752c02b
This commit is contained in:
Andrew Poelstra 2023-09-21 22:14:25 +00:00
commit 675fd54c95
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
6 changed files with 40 additions and 89 deletions

View File

@ -16,9 +16,9 @@ checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
[[package]] [[package]]
name = "bech32" name = "bech32"
version = "0.9.0" version = "0.10.0-alpha"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5738be7561b0eeb501ef1d5c5db3f24e01ceb55fededd9b00039aada34966ad" checksum = "81cc1dec4c25e5a78c52802eda8e2adf0d2aca57ffc536326b75c0e531ea0a9b"
[[package]] [[package]]
name = "bincode" name = "bincode"

View File

@ -16,9 +16,9 @@ checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
[[package]] [[package]]
name = "bech32" name = "bech32"
version = "0.9.1" version = "0.10.0-alpha"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" checksum = "81cc1dec4c25e5a78c52802eda8e2adf0d2aca57ffc536326b75c0e531ea0a9b"
[[package]] [[package]]
name = "bincode" name = "bincode"

View File

@ -27,7 +27,7 @@ bitcoinconsensus-std = ["bitcoinconsensus/std", "std"]
# Instead no-std enables additional features required for this crate to be usable without std. # Instead no-std enables additional features required for this crate to be usable without std.
# As a result, both can be enabled without conflict. # As a result, both can be enabled without conflict.
std = ["secp256k1/std", "hashes/std", "bech32/std", "internals/std", "hex/std"] std = ["secp256k1/std", "hashes/std", "bech32/std", "internals/std", "hex/std"]
no-std = ["core2", "hashes/alloc", "hashes/core2", "secp256k1/alloc", "hex/alloc", "hex/core2"] no-std = ["core2", "hashes/alloc", "hashes/core2", "bech32/alloc", "secp256k1/alloc", "hex/alloc", "hex/core2"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
@ -36,7 +36,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies] [dependencies]
internals = { package = "bitcoin-internals", version = "0.2.0" } internals = { package = "bitcoin-internals", version = "0.2.0" }
hex = { package = "hex-conservative", version = "0.1.1", default-features = false } hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
bech32 = { version = "0.9.0", default-features = false } bech32 = { version = "0.10.0-alpha", default-features = false }
hashes = { package = "bitcoin_hashes", version = "0.13.0", default-features = false } hashes = { package = "bitcoin_hashes", version = "0.13.0", default-features = false }
secp256k1 = { version = "0.27.0", default-features = false, features = ["bitcoin_hashes"] } secp256k1 = { version = "0.27.0", default-features = false, features = ["bitcoin_hashes"] }
hex_lit = "0.1.1" hex_lit = "0.1.1"

View File

@ -99,17 +99,8 @@ impl_std_error!(UnknownAddressTypeError);
pub enum ParseError { pub enum ParseError {
/// Base58 error. /// Base58 error.
Base58(base58::Error), Base58(base58::Error),
/// Bech32 error. /// Bech32 segwit decoding error.
Bech32(bech32::Error), Bech32(bech32::primitives::decode::SegwitHrpstringError),
/// The bech32 payload was empty.
EmptyBech32Payload,
/// The wrong checksum algorithm was used. See BIP-0350.
InvalidBech32Variant {
/// Bech32 variant that is required by the used Witness version.
expected: bech32::Variant,
/// The actual Bech32 variant encoded in the address representation.
found: bech32::Variant,
},
/// A witness version conversion/parsing error. /// A witness version conversion/parsing error.
WitnessVersion(witness_version::TryFromError), WitnessVersion(witness_version::TryFromError),
/// A witness program error. /// A witness program error.
@ -122,13 +113,7 @@ impl fmt::Display for ParseError {
match *self { match *self {
Base58(ref e) => write_err!(f, "base58 error"; e), Base58(ref e) => write_err!(f, "base58 error"; e),
Bech32(ref e) => write_err!(f, "bech32 error"; e), Bech32(ref e) => write_err!(f, "bech32 segwit decoding error"; e),
EmptyBech32Payload => write!(f, "the bech32 payload was empty"),
InvalidBech32Variant { expected, found } => write!(
f,
"invalid bech32 checksum variant found {:?} when {:?} was expected",
found, expected
),
WitnessVersion(ref e) => write_err!(f, "witness version conversion/parsing error"; e), WitnessVersion(ref e) => write_err!(f, "witness version conversion/parsing error"; e),
WitnessProgram(ref e) => write_err!(f, "witness program error"; e), WitnessProgram(ref e) => write_err!(f, "witness program error"; e),
} }
@ -145,7 +130,6 @@ impl std::error::Error for ParseError {
Bech32(ref e) => Some(e), Bech32(ref e) => Some(e),
WitnessVersion(ref e) => Some(e), WitnessVersion(ref e) => Some(e),
WitnessProgram(ref e) => Some(e), WitnessProgram(ref e) => Some(e),
EmptyBech32Payload | InvalidBech32Variant { .. } => None,
} }
} }
} }
@ -154,8 +138,8 @@ impl From<base58::Error> for ParseError {
fn from(e: base58::Error) -> Self { Self::Base58(e) } fn from(e: base58::Error) -> Self { Self::Base58(e) }
} }
impl From<bech32::Error> for ParseError { impl From<bech32::primitives::decode::SegwitHrpstringError> for ParseError {
fn from(e: bech32::Error) -> Self { Self::Bech32(e) } fn from(e: bech32::primitives::decode::SegwitHrpstringError) -> Self { Self::Bech32(e) }
} }
impl From<witness_version::TryFromError> for ParseError { impl From<witness_version::TryFromError> for ParseError {

View File

@ -31,7 +31,7 @@ use core::fmt;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::str::FromStr; use core::str::FromStr;
use bech32; use bech32::primitives::hrp::{self, Hrp};
use hashes::{sha256, Hash, HashEngine}; use hashes::{sha256, Hash, HashEngine};
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey}; use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
@ -46,6 +46,7 @@ use crate::blockdata::script::{self, Script, ScriptBuf, ScriptHash};
use crate::crypto::key::{PubkeyHash, PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey}; use crate::crypto::key::{PubkeyHash, PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey};
use crate::network::Network; use crate::network::Network;
use crate::prelude::*; use crate::prelude::*;
use crate::script::PushBytesBuf;
use crate::taproot::TapNodeHash; use crate::taproot::TapNodeHash;
/// Error code for the address module. /// Error code for the address module.
@ -237,8 +238,8 @@ pub struct AddressEncoding<'a> {
pub p2pkh_prefix: u8, pub p2pkh_prefix: u8,
/// base58 version byte for p2sh payloads (e.g. 0x05 for "3..." addresses). /// base58 version byte for p2sh payloads (e.g. 0x05 for "3..." addresses).
pub p2sh_prefix: u8, pub p2sh_prefix: u8,
/// hrp used in bech32 addresss (e.g. "bc" for "bc1..." addresses). /// The bech32 human-readable part.
pub bech32_hrp: &'a str, pub hrp: Hrp,
} }
/// Formats bech32 as upper case if alternate formatting is chosen (`{:#}`). /// Formats bech32 as upper case if alternate formatting is chosen (`{:#}`).
@ -257,19 +258,16 @@ impl<'a> fmt::Display for AddressEncoding<'a> {
prefixed[1..].copy_from_slice(&hash[..]); prefixed[1..].copy_from_slice(&hash[..]);
base58::encode_check_to_fmt(fmt, &prefixed[..]) base58::encode_check_to_fmt(fmt, &prefixed[..])
} }
Payload::WitnessProgram(witness_prog) => { Payload::WitnessProgram(witness_program) => {
let (version, prog) = (witness_prog.version(), witness_prog.program()); let hrp = &self.hrp;
let mut upper_writer; let version = witness_program.version().to_fe();
let writer = if fmt.alternate() { let program = witness_program.program().as_bytes();
upper_writer = UpperWriter(fmt);
&mut upper_writer as &mut dyn fmt::Write if fmt.alternate() {
bech32::segwit::encode_to_fmt_unchecked_uppercase(fmt, hrp, version, program)
} else { } else {
fmt as &mut dyn fmt::Write bech32::segwit::encode_to_fmt_unchecked(fmt, hrp, version, program)
}; }
let mut bech32_writer =
bech32::Bech32Writer::new(self.bech32_hrp, version.bech32_variant(), writer)?;
bech32::WriteBase32::write_u5(&mut bech32_writer, version.into())?;
bech32::ToBase32::write_base32(&prog.as_bytes(), &mut bech32_writer)
} }
} }
} }
@ -495,13 +493,12 @@ impl<V: NetworkValidation> Address<V> {
Network::Bitcoin => SCRIPT_ADDRESS_PREFIX_MAIN, Network::Bitcoin => SCRIPT_ADDRESS_PREFIX_MAIN,
Network::Testnet | Network::Signet | Network::Regtest => SCRIPT_ADDRESS_PREFIX_TEST, Network::Testnet | Network::Signet | Network::Regtest => SCRIPT_ADDRESS_PREFIX_TEST,
}; };
let bech32_hrp = match self.network() { let hrp = match self.network() {
Network::Bitcoin => "bc", Network::Bitcoin => hrp::BC,
Network::Testnet | Network::Signet => "tb", Network::Testnet | Network::Signet => hrp::TB,
Network::Regtest => "bcrt", Network::Regtest => hrp::BCRT,
}; };
let encoding = let encoding = AddressEncoding { payload: self.payload(), p2pkh_prefix, p2sh_prefix, hrp };
AddressEncoding { payload: self.payload(), p2pkh_prefix, p2sh_prefix, bech32_hrp };
use fmt::Display; use fmt::Display;
@ -775,17 +772,6 @@ impl<V: NetworkValidation> fmt::Debug for Address<V> {
} }
} }
struct UpperWriter<W: fmt::Write>(W);
impl<W: fmt::Write> fmt::Write for UpperWriter<W> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
self.0.write_char(c.to_ascii_uppercase())?;
}
Ok(())
}
}
/// Extracts the bech32 prefix. /// Extracts the bech32 prefix.
/// ///
/// # Returns /// # Returns
@ -812,26 +798,11 @@ impl FromStr for Address<NetworkUnchecked> {
_ => None, _ => None,
}; };
if let Some(network) = bech32_network { if let Some(network) = bech32_network {
// decode as bech32 let (_hrp, version, data) = bech32::segwit::decode(s)?;
let (_, payload, variant) = bech32::decode(s)?; let version = WitnessVersion::try_from(version).expect("we know this is in range 0-16");
if payload.is_empty() { let program = PushBytesBuf::try_from(data).expect("decode() guarantees valid length");
return Err(ParseError::EmptyBech32Payload);
}
// Get the script version and program (converted from 5-bit to 8-bit)
let (version, program): (WitnessVersion, Vec<u8>) = {
let (v, p5) = payload.split_at(1);
(WitnessVersion::try_from(v[0])?, bech32::FromBase32::from_base32(p5)?)
};
let witness_program = WitnessProgram::new(version, program)?; let witness_program = WitnessProgram::new(version, program)?;
// Encoding check
let expected = version.bech32_variant();
if expected != variant {
return Err(ParseError::InvalidBech32Variant { expected, found: variant });
}
return Ok(Address::new(network, Payload::WitnessProgram(witness_program))); return Ok(Address::new(network, Payload::WitnessProgram(witness_program)));
} }

View File

@ -11,6 +11,7 @@ use core::convert::TryFrom;
use core::fmt; use core::fmt;
use core::str::FromStr; use core::str::FromStr;
use bech32::Fe32;
use internals::write_err; use internals::write_err;
use crate::blockdata::opcodes::all::*; use crate::blockdata::opcodes::all::*;
@ -72,12 +73,9 @@ impl WitnessVersion {
/// into a byte since the conversion requires context (bitcoin script or just a version number). /// into a byte since the conversion requires context (bitcoin script or just a version number).
pub fn to_num(self) -> u8 { self as u8 } pub fn to_num(self) -> u8 { self as u8 }
/// Determines the checksum variant. See BIP-0350 for specification. /// Converts this witness version to a GF32 field element.
pub fn bech32_variant(&self) -> bech32::Variant { pub fn to_fe(self) -> Fe32 {
match self { Fe32::try_from(self.to_num()).expect("0-16 are valid fe32 values")
WitnessVersion::V0 => bech32::Variant::Bech32,
_ => bech32::Variant::Bech32m,
}
} }
} }
@ -95,10 +93,10 @@ impl FromStr for WitnessVersion {
} }
} }
impl TryFrom<bech32::u5> for WitnessVersion { impl TryFrom<bech32::Fe32> for WitnessVersion {
type Error = TryFromError; type Error = TryFromError;
fn try_from(value: bech32::u5) -> Result<Self, Self::Error> { Self::try_from(value.to_u8()) } fn try_from(value: Fe32) -> Result<Self, Self::Error> { Self::try_from(value.to_u8()) }
} }
impl TryFrom<u8> for WitnessVersion { impl TryFrom<u8> for WitnessVersion {
@ -155,10 +153,8 @@ impl<'a> TryFrom<Instruction<'a>> for WitnessVersion {
} }
} }
impl From<WitnessVersion> for bech32::u5 { impl From<WitnessVersion> for Fe32 {
fn from(version: WitnessVersion) -> Self { fn from(version: WitnessVersion) -> Self { version.to_fe() }
bech32::u5::try_from_u8(version.to_num()).expect("WitnessVersion must be 0..=16")
}
} }
impl From<WitnessVersion> for Opcode { impl From<WitnessVersion> for Opcode {