Decode an address string based on prefix
When a decoding error occurs for a bech32 address string the error is discarded and the same address string is attempted to be decoded as base58. This then incorrectly returns a base58 error. Check the string prefix and decode as bech32 or base58 and return the relevant error. If the prefix is unknown return an `UnknownHrpError`.
This commit is contained in:
parent
02bf38e2a6
commit
3d994f7bdb
|
@ -799,47 +799,21 @@ impl Address<NetworkUnchecked> {
|
||||||
};
|
};
|
||||||
Address(inner, PhantomData)
|
Address(inner, PhantomData)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Address> for ScriptBuf {
|
/// Parse a bech32 Address string
|
||||||
fn from(a: Address) -> Self { a.script_pubkey() }
|
pub fn from_bech32_str(s: &str) -> Result<Address<NetworkUnchecked>, ParseError> {
|
||||||
}
|
let (hrp, witness_version, data) = bech32::segwit::decode(s)?;
|
||||||
|
|
||||||
// Alternate formatting `{:#}` is used to return uppercase version of bech32 addresses which should
|
|
||||||
// be used in QR codes, see [`Address::to_qr_uri`].
|
|
||||||
impl fmt::Display for Address {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, fmt) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: NetworkValidation> fmt::Debug for Address<V> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
if V::IS_CHECKED {
|
|
||||||
fmt::Display::fmt(&self.0, f)
|
|
||||||
} else {
|
|
||||||
write!(f, "Address<NetworkUnchecked>(")?;
|
|
||||||
fmt::Display::fmt(&self.0, f)?;
|
|
||||||
write!(f, ")")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Address can be parsed only with `NetworkUnchecked`.
|
|
||||||
impl FromStr for Address<NetworkUnchecked> {
|
|
||||||
type Err = ParseError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Address<NetworkUnchecked>, ParseError> {
|
|
||||||
if let Ok((hrp, witness_version, data)) = bech32::segwit::decode(s) {
|
|
||||||
let version = WitnessVersion::try_from(witness_version.to_u8())?;
|
let version = WitnessVersion::try_from(witness_version.to_u8())?;
|
||||||
let program = WitnessProgram::new(version, &data)
|
let program = WitnessProgram::new(version, &data)
|
||||||
.expect("bech32 guarantees valid program length for witness");
|
.expect("bech32 guarantees valid program length for witness");
|
||||||
|
|
||||||
let hrp = KnownHrp::from_hrp(hrp)?;
|
let hrp = KnownHrp::from_hrp(hrp)?;
|
||||||
let inner = AddressInner::Segwit { program, hrp };
|
let inner = AddressInner::Segwit { program, hrp };
|
||||||
return Ok(Address(inner, PhantomData));
|
Ok(Address(inner, PhantomData))
|
||||||
}
|
}
|
||||||
|
|
||||||
// If segwit decoding fails, assume its a legacy address.
|
/// Parse a base58 Address string
|
||||||
|
pub fn from_base58_str(s: &str) -> Result<Address<NetworkUnchecked>, ParseError> {
|
||||||
if s.len() > 50 {
|
if s.len() > 50 {
|
||||||
return Err(LegacyAddressTooLongError { length: s.len() }.into());
|
return Err(LegacyAddressTooLongError { length: s.len() }.into());
|
||||||
}
|
}
|
||||||
|
@ -875,6 +849,50 @@ impl FromStr for Address<NetworkUnchecked> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Address> for ScriptBuf {
|
||||||
|
fn from(a: Address) -> Self { a.script_pubkey() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alternate formatting `{:#}` is used to return uppercase version of bech32 addresses which should
|
||||||
|
// be used in QR codes, see [`Address::to_qr_uri`].
|
||||||
|
impl fmt::Display for Address {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, fmt) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: NetworkValidation> fmt::Debug for Address<V> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
if V::IS_CHECKED {
|
||||||
|
fmt::Display::fmt(&self.0, f)
|
||||||
|
} else {
|
||||||
|
write!(f, "Address<NetworkUnchecked>(")?;
|
||||||
|
fmt::Display::fmt(&self.0, f)?;
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Address can be parsed only with `NetworkUnchecked`.
|
||||||
|
///
|
||||||
|
/// Only segwit bech32 addresses prefixed with `bc`, `bcrt` or `tb` and legacy base58 addresses
|
||||||
|
/// prefixed with `1`, `2, `3`, `m` or `n` are supported.
|
||||||
|
impl FromStr for Address<NetworkUnchecked> {
|
||||||
|
type Err = ParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Address<NetworkUnchecked>, ParseError> {
|
||||||
|
if ["bc1", "bcrt1", "tb1"].iter().any(|&prefix| s.to_lowercase().starts_with(prefix)) {
|
||||||
|
Address::from_bech32_str(s)
|
||||||
|
} else if ["1", "2", "3", "m", "n"].iter().any(|&prefix| s.starts_with(prefix)) {
|
||||||
|
Address::from_base58_str(s)
|
||||||
|
} else {
|
||||||
|
let hrp = match s.rfind('1') {
|
||||||
|
Some(pos) => &s[..pos],
|
||||||
|
None => s,
|
||||||
|
};
|
||||||
|
Err(UnknownHrpError(hrp.to_owned()).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert a byte array of a pubkey hash into a segwit redeem hash
|
/// Convert a byte array of a pubkey hash into a segwit redeem hash
|
||||||
fn segwit_redeem_hash(pubkey_hash: PubkeyHash) -> hash160::Hash {
|
fn segwit_redeem_hash(pubkey_hash: PubkeyHash) -> hash160::Hash {
|
||||||
let mut sha_engine = hash160::Hash::engine();
|
let mut sha_engine = hash160::Hash::engine();
|
||||||
|
|
Loading…
Reference in New Issue