Use the new bech32 iterator API

Use the new bech32 iterator API that Andrew and I wrote.
This commit is contained in:
Tobin C. Harding 2023-08-04 11:05:15 +10:00
parent a2a4efbe6a
commit e4c7e01a6f
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
6 changed files with 40 additions and 89 deletions

View File

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

View File

@ -16,9 +16,9 @@ checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
[[package]]
name = "bech32"
version = "0.9.1"
version = "0.10.0-alpha"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
checksum = "81cc1dec4c25e5a78c52802eda8e2adf0d2aca57ffc536326b75c0e531ea0a9b"
[[package]]
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.
# As a result, both can be enabled without conflict.
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]
all-features = true
@ -36,7 +36,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
internals = { package = "bitcoin-internals", version = "0.2.0" }
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 }
secp256k1 = { version = "0.27.0", default-features = false, features = ["bitcoin_hashes"] }
hex_lit = "0.1.1"

View File

@ -99,17 +99,8 @@ impl_std_error!(UnknownAddressTypeError);
pub enum ParseError {
/// Base58 error.
Base58(base58::Error),
/// Bech32 error.
Bech32(bech32::Error),
/// 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,
},
/// Bech32 segwit decoding error.
Bech32(bech32::primitives::decode::SegwitHrpstringError),
/// A witness version conversion/parsing error.
WitnessVersion(witness_version::TryFromError),
/// A witness program error.
@ -122,13 +113,7 @@ impl fmt::Display for ParseError {
match *self {
Base58(ref e) => write_err!(f, "base58 error"; e),
Bech32(ref e) => write_err!(f, "bech32 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
),
Bech32(ref e) => write_err!(f, "bech32 segwit decoding error"; e),
WitnessVersion(ref e) => write_err!(f, "witness version conversion/parsing 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),
WitnessVersion(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) }
}
impl From<bech32::Error> for ParseError {
fn from(e: bech32::Error) -> Self { Self::Bech32(e) }
impl From<bech32::primitives::decode::SegwitHrpstringError> for ParseError {
fn from(e: bech32::primitives::decode::SegwitHrpstringError) -> Self { Self::Bech32(e) }
}
impl From<witness_version::TryFromError> for ParseError {

View File

@ -31,7 +31,7 @@ use core::fmt;
use core::marker::PhantomData;
use core::str::FromStr;
use bech32;
use bech32::primitives::hrp::{self, Hrp};
use hashes::{sha256, Hash, HashEngine};
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::network::Network;
use crate::prelude::*;
use crate::script::PushBytesBuf;
use crate::taproot::TapNodeHash;
/// Error code for the address module.
@ -237,8 +238,8 @@ pub struct AddressEncoding<'a> {
pub p2pkh_prefix: u8,
/// base58 version byte for p2sh payloads (e.g. 0x05 for "3..." addresses).
pub p2sh_prefix: u8,
/// hrp used in bech32 addresss (e.g. "bc" for "bc1..." addresses).
pub bech32_hrp: &'a str,
/// The bech32 human-readable part.
pub hrp: Hrp,
}
/// 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[..]);
base58::encode_check_to_fmt(fmt, &prefixed[..])
}
Payload::WitnessProgram(witness_prog) => {
let (version, prog) = (witness_prog.version(), witness_prog.program());
let mut upper_writer;
let writer = if fmt.alternate() {
upper_writer = UpperWriter(fmt);
&mut upper_writer as &mut dyn fmt::Write
Payload::WitnessProgram(witness_program) => {
let hrp = &self.hrp;
let version = witness_program.version().to_fe();
let program = witness_program.program().as_bytes();
if fmt.alternate() {
bech32::segwit::encode_to_fmt_unchecked_uppercase(fmt, hrp, version, program)
} else {
fmt as &mut dyn fmt::Write
};
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)
bech32::segwit::encode_to_fmt_unchecked(fmt, hrp, version, program)
}
}
}
}
@ -495,13 +493,12 @@ impl<V: NetworkValidation> Address<V> {
Network::Bitcoin => SCRIPT_ADDRESS_PREFIX_MAIN,
Network::Testnet | Network::Signet | Network::Regtest => SCRIPT_ADDRESS_PREFIX_TEST,
};
let bech32_hrp = match self.network() {
Network::Bitcoin => "bc",
Network::Testnet | Network::Signet => "tb",
Network::Regtest => "bcrt",
let hrp = match self.network() {
Network::Bitcoin => hrp::BC,
Network::Testnet | Network::Signet => hrp::TB,
Network::Regtest => hrp::BCRT,
};
let encoding =
AddressEncoding { payload: self.payload(), p2pkh_prefix, p2sh_prefix, bech32_hrp };
let encoding = AddressEncoding { payload: self.payload(), p2pkh_prefix, p2sh_prefix, hrp };
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.
///
/// # Returns
@ -812,26 +798,11 @@ impl FromStr for Address<NetworkUnchecked> {
_ => None,
};
if let Some(network) = bech32_network {
// decode as bech32
let (_, payload, variant) = bech32::decode(s)?;
if payload.is_empty() {
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 (_hrp, version, data) = bech32::segwit::decode(s)?;
let version = WitnessVersion::try_from(version).expect("we know this is in range 0-16");
let program = PushBytesBuf::try_from(data).expect("decode() guarantees valid length");
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)));
}

View File

@ -11,6 +11,7 @@ use core::convert::TryFrom;
use core::fmt;
use core::str::FromStr;
use bech32::Fe32;
use internals::write_err;
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).
pub fn to_num(self) -> u8 { self as u8 }
/// Determines the checksum variant. See BIP-0350 for specification.
pub fn bech32_variant(&self) -> bech32::Variant {
match self {
WitnessVersion::V0 => bech32::Variant::Bech32,
_ => bech32::Variant::Bech32m,
}
/// Converts this witness version to a GF32 field element.
pub fn to_fe(self) -> Fe32 {
Fe32::try_from(self.to_num()).expect("0-16 are valid fe32 values")
}
}
@ -95,10 +93,10 @@ impl FromStr for WitnessVersion {
}
}
impl TryFrom<bech32::u5> for WitnessVersion {
impl TryFrom<bech32::Fe32> for WitnessVersion {
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 {
@ -155,10 +153,8 @@ impl<'a> TryFrom<Instruction<'a>> for WitnessVersion {
}
}
impl From<WitnessVersion> for bech32::u5 {
fn from(version: WitnessVersion) -> Self {
bech32::u5::try_from_u8(version.to_num()).expect("WitnessVersion must be 0..=16")
}
impl From<WitnessVersion> for Fe32 {
fn from(version: WitnessVersion) -> Self { version.to_fe() }
}
impl From<WitnessVersion> for Opcode {