Use the new bech32 iterator API
Use the new bech32 iterator API that Andrew and I wrote.
This commit is contained in:
parent
a2a4efbe6a
commit
e4c7e01a6f
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue