Skip to content

Commit

Permalink
Use the new bech32 iterator API
Browse files Browse the repository at this point in the history
Use the new bech32 iterator API that Andrew and I wrote.
  • Loading branch information
tcharding committed Sep 20, 2023
1 parent fa42500 commit b85a0c9
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 73 deletions.
4 changes: 2 additions & 2 deletions Cargo-minimal.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions Cargo-recent.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions bitcoin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand Down
10 changes: 5 additions & 5 deletions bitcoin/src/address/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ impl_std_error!(UnknownAddressTypeError);
pub enum ParseError {
/// Base58 error.
Base58(base58::Error),
/// Bech32 error.
Bech32(bech32::Error),
/// Bech32 segwit decoding error.
Bech32(bech32::primitives::decode::SegwitHrpstringError),
/// The bech32 payload was empty.
EmptyBech32Payload,
/// The wrong checksum algorithm was used. See BIP-0350.
Expand All @@ -122,7 +122,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),
Bech32(ref e) => write_err!(f, "bech32 segwit decoding error"; e),
EmptyBech32Payload => write!(f, "the bech32 payload was empty"),
InvalidBech32Variant { expected, found } => write!(
f,
Expand Down Expand Up @@ -154,8 +154,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 {
Expand Down
71 changes: 21 additions & 50 deletions bitcoin/src/address/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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.
Expand Down Expand Up @@ -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 (`{:#}`).
Expand All @@ -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)
}
}
}
}
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand All @@ -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)));
}

Expand Down
20 changes: 8 additions & 12 deletions bitcoin/src/blockdata/script/witness_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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")
}
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit b85a0c9

Please sign in to comment.