Merge rust-bitcoin/rust-bitcoin#1975: Prepare for using new bech32 release
52f2332383
Remove docs from witness version conversion functions (Tobin C. Harding)47d6d785cb
Remove bip 173/350 test vectors (Tobin C. Harding)e0eaeaad99
Split ParseError out of Error (Tobin C. Harding)0f536e86dc
Add new UnknownAddressTypeError for parsing address type (Tobin C. Harding)e2014cba1b
Import error variants within dislay impl (Tobin C. Harding)9d7791fcd6
Remove unnecessary self:: from error import (Tobin C. Harding)b2e485ed51
Split the address error code out into a separate module (Tobin C. Harding)f34ca0c52b
Move address.rs to address/mod.rs (Tobin C. Harding) Pull request description: In preparation for depending on the recently released version of `rust-bech32` do a bunch of preparatory fixes. 1. Improve `address` module error handling as we are doing else where at the moment 2. Remove bip 173 and 350 test vector tests, these are fully covered in bech32 3. Trim down the docs on `WitnessVersion` This PR is the first 8 patches of https://github.com/rust-bitcoin/rust-bitcoin/pull/1951 ACKs for top commit: sanket1729: ACK52f2332383
apoelstra: ACK52f2332383
Tree-SHA512: 67a4ea4020b4e5c9c8396e4195e06dbd1d11335788f9e52f60abbc0b399e37e5dacc9bb7fa4e88221670322fa3c3407ade059d5c709f96e2df97240f4524e08c
This commit is contained in:
commit
a2a4efbe6a
|
@ -0,0 +1,167 @@
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
use internals::write_err;
|
||||||
|
|
||||||
|
use crate::address::{Address, NetworkUnchecked};
|
||||||
|
use crate::blockdata::script::{witness_program, witness_version};
|
||||||
|
use crate::error::impl_std_error;
|
||||||
|
use crate::prelude::String;
|
||||||
|
use crate::{base58, Network};
|
||||||
|
|
||||||
|
/// Address error.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Error {
|
||||||
|
/// A witness version construction error.
|
||||||
|
WitnessVersion(witness_version::TryFromError),
|
||||||
|
/// A witness program error.
|
||||||
|
WitnessProgram(witness_program::Error),
|
||||||
|
/// An uncompressed pubkey was used where it is not allowed.
|
||||||
|
UncompressedPubkey,
|
||||||
|
/// Address size more than 520 bytes is not allowed.
|
||||||
|
ExcessiveScriptSize,
|
||||||
|
/// Script is not a p2pkh, p2sh or witness program.
|
||||||
|
UnrecognizedScript,
|
||||||
|
/// Address's network differs from required one.
|
||||||
|
NetworkValidation {
|
||||||
|
/// Network that was required.
|
||||||
|
required: Network,
|
||||||
|
/// Network on which the address was found to be valid.
|
||||||
|
found: Network,
|
||||||
|
/// The address itself
|
||||||
|
address: Address<NetworkUnchecked>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use Error::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
WitnessVersion(ref e) => write_err!(f, "witness version construction error"; e),
|
||||||
|
WitnessProgram(ref e) => write_err!(f, "witness program error"; e),
|
||||||
|
UncompressedPubkey =>
|
||||||
|
write!(f, "an uncompressed pubkey was used where it is not allowed"),
|
||||||
|
ExcessiveScriptSize => write!(f, "script size exceed 520 bytes"),
|
||||||
|
UnrecognizedScript => write!(f, "script is not a p2pkh, p2sh or witness program"),
|
||||||
|
NetworkValidation { required, found, ref address } => {
|
||||||
|
write!(f, "address ")?;
|
||||||
|
address.fmt_internal(f)?; // Using fmt_internal in order to remove the "Address<NetworkUnchecked>(..)" wrapper
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
" belongs to network {} which is different from required {}",
|
||||||
|
found, required
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
use Error::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
WitnessVersion(e) => Some(e),
|
||||||
|
WitnessProgram(e) => Some(e),
|
||||||
|
UncompressedPubkey
|
||||||
|
| ExcessiveScriptSize
|
||||||
|
| UnrecognizedScript
|
||||||
|
| NetworkValidation { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<witness_version::TryFromError> for Error {
|
||||||
|
fn from(e: witness_version::TryFromError) -> Error { Error::WitnessVersion(e) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<witness_program::Error> for Error {
|
||||||
|
fn from(e: witness_program::Error) -> Error { Error::WitnessProgram(e) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Address type is either invalid or not supported in rust-bitcoin.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct UnknownAddressTypeError(pub String);
|
||||||
|
|
||||||
|
impl fmt::Display for UnknownAddressTypeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write_err!(f, "failed to parse {} as address type", self.0; self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_std_error!(UnknownAddressTypeError);
|
||||||
|
|
||||||
|
/// Address parsing error.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
/// A witness version conversion/parsing error.
|
||||||
|
WitnessVersion(witness_version::TryFromError),
|
||||||
|
/// A witness program error.
|
||||||
|
WitnessProgram(witness_program::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ParseError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use 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
|
||||||
|
),
|
||||||
|
WitnessVersion(ref e) => write_err!(f, "witness version conversion/parsing error"; e),
|
||||||
|
WitnessProgram(ref e) => write_err!(f, "witness program error"; e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl std::error::Error for ParseError {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
use ParseError::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Base58(ref e) => Some(e),
|
||||||
|
Bech32(ref e) => Some(e),
|
||||||
|
WitnessVersion(ref e) => Some(e),
|
||||||
|
WitnessProgram(ref e) => Some(e),
|
||||||
|
EmptyBech32Payload | InvalidBech32Variant { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<witness_version::TryFromError> for ParseError {
|
||||||
|
fn from(e: witness_version::TryFromError) -> Self { Self::WitnessVersion(e) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<witness_program::Error> for ParseError {
|
||||||
|
fn from(e: witness_program::Error) -> Self { Self::WitnessProgram(e) }
|
||||||
|
}
|
|
@ -33,7 +33,6 @@ use core::str::FromStr;
|
||||||
|
|
||||||
use bech32;
|
use bech32;
|
||||||
use hashes::{sha256, Hash, HashEngine};
|
use hashes::{sha256, Hash, HashEngine};
|
||||||
use internals::write_err;
|
|
||||||
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
|
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
|
||||||
|
|
||||||
use crate::base58;
|
use crate::base58;
|
||||||
|
@ -41,126 +40,17 @@ use crate::blockdata::constants::{
|
||||||
MAX_SCRIPT_ELEMENT_SIZE, PUBKEY_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST,
|
MAX_SCRIPT_ELEMENT_SIZE, PUBKEY_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST,
|
||||||
SCRIPT_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_TEST,
|
SCRIPT_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_TEST,
|
||||||
};
|
};
|
||||||
use crate::blockdata::script::witness_program::{self, WitnessProgram};
|
use crate::blockdata::script::witness_program::WitnessProgram;
|
||||||
use crate::blockdata::script::witness_version::{self, WitnessVersion};
|
use crate::blockdata::script::witness_version::WitnessVersion;
|
||||||
use crate::blockdata::script::{self, Script, ScriptBuf, ScriptHash};
|
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::taproot::TapNodeHash;
|
use crate::taproot::TapNodeHash;
|
||||||
|
|
||||||
/// Address error.
|
/// Error code for the address module.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
pub mod error;
|
||||||
#[non_exhaustive]
|
pub use self::error::{Error, ParseError, UnknownAddressTypeError};
|
||||||
pub enum Error {
|
|
||||||
/// Base58 encoding error.
|
|
||||||
Base58(base58::Error),
|
|
||||||
/// Bech32 encoding 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,
|
|
||||||
},
|
|
||||||
/// A witness version construction error.
|
|
||||||
WitnessVersion(witness_version::TryFromError),
|
|
||||||
/// A witness program error.
|
|
||||||
WitnessProgram(witness_program::Error),
|
|
||||||
/// An uncompressed pubkey was used where it is not allowed.
|
|
||||||
UncompressedPubkey,
|
|
||||||
/// Address size more than 520 bytes is not allowed.
|
|
||||||
ExcessiveScriptSize,
|
|
||||||
/// Script is not a p2pkh, p2sh or witness program.
|
|
||||||
UnrecognizedScript,
|
|
||||||
/// Address type is either invalid or not supported in rust-bitcoin.
|
|
||||||
UnknownAddressType(String),
|
|
||||||
/// Address's network differs from required one.
|
|
||||||
NetworkValidation {
|
|
||||||
/// Network that was required.
|
|
||||||
required: Network,
|
|
||||||
/// Network on which the address was found to be valid.
|
|
||||||
found: Network,
|
|
||||||
/// The address itself
|
|
||||||
address: Address<NetworkUnchecked>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
Error::Base58(ref e) => write_err!(f, "base58 address encoding error"; e),
|
|
||||||
Error::Bech32(ref e) => write_err!(f, "bech32 address encoding error"; e),
|
|
||||||
Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"),
|
|
||||||
Error::InvalidBech32Variant { expected, found } => write!(
|
|
||||||
f,
|
|
||||||
"invalid bech32 checksum variant found {:?} when {:?} was expected",
|
|
||||||
found, expected
|
|
||||||
),
|
|
||||||
Error::WitnessVersion(ref e) => write_err!(f, "witness version construction error"; e),
|
|
||||||
Error::WitnessProgram(ref e) => write_err!(f, "witness program error"; e),
|
|
||||||
Error::UncompressedPubkey =>
|
|
||||||
write!(f, "an uncompressed pubkey was used where it is not allowed"),
|
|
||||||
Error::ExcessiveScriptSize => write!(f, "script size exceed 520 bytes"),
|
|
||||||
Error::UnrecognizedScript =>
|
|
||||||
write!(f, "script is not a p2pkh, p2sh or witness program"),
|
|
||||||
Error::UnknownAddressType(ref s) => write!(
|
|
||||||
f,
|
|
||||||
"unknown address type: '{}' is either invalid or not supported in rust-bitcoin",
|
|
||||||
s
|
|
||||||
),
|
|
||||||
Error::NetworkValidation { required, found, ref address } => {
|
|
||||||
write!(f, "address ")?;
|
|
||||||
address.fmt_internal(f)?; // Using fmt_internal in order to remove the "Address<NetworkUnchecked>(..)" wrapper
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
" belongs to network {} which is different from required {}",
|
|
||||||
found, required
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl std::error::Error for Error {
|
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
||||||
use self::Error::*;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Base58(e) => Some(e),
|
|
||||||
Bech32(e) => Some(e),
|
|
||||||
WitnessVersion(e) => Some(e),
|
|
||||||
WitnessProgram(e) => Some(e),
|
|
||||||
EmptyBech32Payload
|
|
||||||
| InvalidBech32Variant { .. }
|
|
||||||
| UncompressedPubkey
|
|
||||||
| ExcessiveScriptSize
|
|
||||||
| UnrecognizedScript
|
|
||||||
| UnknownAddressType(_)
|
|
||||||
| NetworkValidation { .. } => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<base58::Error> for Error {
|
|
||||||
fn from(e: base58::Error) -> Error { Error::Base58(e) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bech32::Error> for Error {
|
|
||||||
fn from(e: bech32::Error) -> Error { Error::Bech32(e) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<witness_version::TryFromError> for Error {
|
|
||||||
fn from(e: witness_version::TryFromError) -> Error { Error::WitnessVersion(e) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<witness_program::Error> for Error {
|
|
||||||
fn from(e: witness_program::Error) -> Error { Error::WitnessProgram(e) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The different types of addresses.
|
/// The different types of addresses.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -191,7 +81,7 @@ impl fmt::Display for AddressType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for AddressType {
|
impl FromStr for AddressType {
|
||||||
type Err = Error;
|
type Err = UnknownAddressTypeError;
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
"p2pkh" => Ok(AddressType::P2pkh),
|
"p2pkh" => Ok(AddressType::P2pkh),
|
||||||
|
@ -199,7 +89,7 @@ impl FromStr for AddressType {
|
||||||
"p2wpkh" => Ok(AddressType::P2wpkh),
|
"p2wpkh" => Ok(AddressType::P2wpkh),
|
||||||
"p2wsh" => Ok(AddressType::P2wsh),
|
"p2wsh" => Ok(AddressType::P2wsh),
|
||||||
"p2tr" => Ok(AddressType::P2tr),
|
"p2tr" => Ok(AddressType::P2tr),
|
||||||
_ => Err(Error::UnknownAddressType(s.to_owned())),
|
_ => Err(UnknownAddressTypeError(s.to_owned())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -910,9 +800,9 @@ fn find_bech32_prefix(bech32: &str) -> &str {
|
||||||
|
|
||||||
/// Address can be parsed only with `NetworkUnchecked`.
|
/// Address can be parsed only with `NetworkUnchecked`.
|
||||||
impl FromStr for Address<NetworkUnchecked> {
|
impl FromStr for Address<NetworkUnchecked> {
|
||||||
type Err = Error;
|
type Err = ParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Address<NetworkUnchecked>, Error> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
// try bech32
|
// try bech32
|
||||||
let bech32_network = match find_bech32_prefix(s) {
|
let bech32_network = match find_bech32_prefix(s) {
|
||||||
// note that upper or lowercase is allowed but NOT mixed case
|
// note that upper or lowercase is allowed but NOT mixed case
|
||||||
|
@ -925,7 +815,7 @@ impl FromStr for Address<NetworkUnchecked> {
|
||||||
// decode as bech32
|
// decode as bech32
|
||||||
let (_, payload, variant) = bech32::decode(s)?;
|
let (_, payload, variant) = bech32::decode(s)?;
|
||||||
if payload.is_empty() {
|
if payload.is_empty() {
|
||||||
return Err(Error::EmptyBech32Payload);
|
return Err(ParseError::EmptyBech32Payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the script version and program (converted from 5-bit to 8-bit)
|
// Get the script version and program (converted from 5-bit to 8-bit)
|
||||||
|
@ -939,7 +829,7 @@ impl FromStr for Address<NetworkUnchecked> {
|
||||||
// Encoding check
|
// Encoding check
|
||||||
let expected = version.bech32_variant();
|
let expected = version.bech32_variant();
|
||||||
if expected != variant {
|
if expected != variant {
|
||||||
return Err(Error::InvalidBech32Variant { expected, found: 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)));
|
||||||
|
@ -947,11 +837,11 @@ impl FromStr for Address<NetworkUnchecked> {
|
||||||
|
|
||||||
// Base58
|
// Base58
|
||||||
if s.len() > 50 {
|
if s.len() > 50 {
|
||||||
return Err(Error::Base58(base58::Error::InvalidLength(s.len() * 11 / 15)));
|
return Err(ParseError::Base58(base58::Error::InvalidLength(s.len() * 11 / 15)));
|
||||||
}
|
}
|
||||||
let data = base58::decode_check(s)?;
|
let data = base58::decode_check(s)?;
|
||||||
if data.len() != 21 {
|
if data.len() != 21 {
|
||||||
return Err(Error::Base58(base58::Error::InvalidLength(data.len())));
|
return Err(ParseError::Base58(base58::Error::InvalidLength(data.len())));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (network, payload) = match data[0] {
|
let (network, payload) = match data[0] {
|
||||||
|
@ -963,7 +853,7 @@ impl FromStr for Address<NetworkUnchecked> {
|
||||||
(Network::Testnet, Payload::PubkeyHash(PubkeyHash::from_slice(&data[1..]).unwrap())),
|
(Network::Testnet, Payload::PubkeyHash(PubkeyHash::from_slice(&data[1..]).unwrap())),
|
||||||
SCRIPT_ADDRESS_PREFIX_TEST =>
|
SCRIPT_ADDRESS_PREFIX_TEST =>
|
||||||
(Network::Testnet, Payload::ScriptHash(ScriptHash::from_slice(&data[1..]).unwrap())),
|
(Network::Testnet, Payload::ScriptHash(ScriptHash::from_slice(&data[1..]).unwrap())),
|
||||||
x => return Err(Error::Base58(base58::Error::InvalidAddressVersion(x))),
|
x => return Err(ParseError::Base58(base58::Error::InvalidAddressVersion(x))),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Address::new(network, payload))
|
Ok(Address::new(network, payload))
|
||||||
|
@ -1193,84 +1083,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bip173_350_vectors() {
|
|
||||||
// Test vectors valid under both BIP-173 and BIP-350
|
|
||||||
let valid_vectors = [
|
|
||||||
("BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", "0014751e76e8199196d454941c45d1b3a323f1433bd6"),
|
|
||||||
("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"),
|
|
||||||
("bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"),
|
|
||||||
("BC1SW50QGDZ25J", "6002751e"),
|
|
||||||
("bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", "5210751e76e8199196d454941c45d1b3a323"),
|
|
||||||
("tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"),
|
|
||||||
("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", "5120000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"),
|
|
||||||
("bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", "512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")
|
|
||||||
];
|
|
||||||
for vector in &valid_vectors {
|
|
||||||
let addr: Address = vector.0.parse::<Address<_>>().unwrap().assume_checked();
|
|
||||||
assert_eq!(&addr.script_pubkey().to_hex_string(), vector.1);
|
|
||||||
roundtrips(&addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
let invalid_vectors = [
|
|
||||||
// 1. BIP-350 test vectors
|
|
||||||
// Invalid human-readable part
|
|
||||||
"tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut",
|
|
||||||
// Invalid checksums (Bech32 instead of Bech32m):
|
|
||||||
"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd",
|
|
||||||
"tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf",
|
|
||||||
"BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL",
|
|
||||||
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh",
|
|
||||||
"tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47",
|
|
||||||
// Invalid character in checksum
|
|
||||||
"bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4",
|
|
||||||
// Invalid witness version
|
|
||||||
"BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R",
|
|
||||||
// Invalid program length (1 byte)
|
|
||||||
"bc1pw5dgrnzv",
|
|
||||||
// Invalid program length (41 bytes)
|
|
||||||
"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav",
|
|
||||||
// Invalid program length for witness version 0 (per BIP141)
|
|
||||||
"BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P",
|
|
||||||
// Mixed case
|
|
||||||
"tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq",
|
|
||||||
// zero padding of more than 4 bits
|
|
||||||
"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf",
|
|
||||||
// Non-zero padding in 8-to-5 conversion
|
|
||||||
"tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j",
|
|
||||||
// Empty data section
|
|
||||||
"bc1gmk9yu",
|
|
||||||
// 2. BIP-173 test vectors
|
|
||||||
// Invalid human-readable part
|
|
||||||
"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty",
|
|
||||||
// Invalid checksum
|
|
||||||
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5",
|
|
||||||
// Invalid witness version
|
|
||||||
"BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2",
|
|
||||||
// Invalid program length
|
|
||||||
"bc1rw5uspcuh",
|
|
||||||
// Invalid program length
|
|
||||||
"bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90",
|
|
||||||
// Invalid program length for witness version 0 (per BIP141)
|
|
||||||
"BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P",
|
|
||||||
// Mixed case
|
|
||||||
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7",
|
|
||||||
// zero padding of more than 4 bits
|
|
||||||
"bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du",
|
|
||||||
// Non-zero padding in 8-to-5 conversion
|
|
||||||
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv",
|
|
||||||
// Final test for empty data section is the same as above in BIP-350
|
|
||||||
|
|
||||||
// 3. BIP-173 valid test vectors obsolete by BIP-350
|
|
||||||
"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx",
|
|
||||||
"BC1SW50QA3JX3S",
|
|
||||||
"bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj",
|
|
||||||
];
|
|
||||||
for vector in &invalid_vectors {
|
|
||||||
assert!(vector.parse::<Address<_>>().is_err());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
fn test_json_serialize() {
|
fn test_json_serialize() {
|
||||||
|
@ -1601,7 +1413,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_address_parses_error() {
|
fn invalid_address_parses_error() {
|
||||||
let got = AddressType::from_str("invalid");
|
let got = AddressType::from_str("invalid");
|
||||||
let want = Err(Error::UnknownAddressType("invalid".to_string()));
|
let want = Err(UnknownAddressTypeError("invalid".to_string()));
|
||||||
assert_eq!(got, want);
|
assert_eq!(got, want);
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,14 +156,12 @@ impl<'a> TryFrom<Instruction<'a>> for WitnessVersion {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<WitnessVersion> for bech32::u5 {
|
impl From<WitnessVersion> for bech32::u5 {
|
||||||
/// Converts [`WitnessVersion`] instance into corresponding Bech32(m) u5-value ([`bech32::u5`]).
|
|
||||||
fn from(version: WitnessVersion) -> Self {
|
fn from(version: WitnessVersion) -> Self {
|
||||||
bech32::u5::try_from_u8(version.to_num()).expect("WitnessVersion must be 0..=16")
|
bech32::u5::try_from_u8(version.to_num()).expect("WitnessVersion must be 0..=16")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<WitnessVersion> for Opcode {
|
impl From<WitnessVersion> for Opcode {
|
||||||
/// Converts [`WitnessVersion`] instance into corresponding Bitcoin scriptopcode (`OP_0`..`OP_16`).
|
|
||||||
fn from(version: WitnessVersion) -> Opcode {
|
fn from(version: WitnessVersion) -> Opcode {
|
||||||
match version {
|
match version {
|
||||||
WitnessVersion::V0 => OP_PUSHBYTES_0,
|
WitnessVersion::V0 => OP_PUSHBYTES_0,
|
||||||
|
|
Loading…
Reference in New Issue