Make `Address` obey sanity rules
`Address` was directly accessing its internals in multiple places. This makes maintenance harder, so change it to use methods instead.
This commit is contained in:
parent
bc6da1fe07
commit
7a115e3cf1
|
@ -158,7 +158,7 @@ pub struct NetworkValidationError {
|
||||||
impl fmt::Display for NetworkValidationError {
|
impl fmt::Display for NetworkValidationError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "address ")?;
|
write!(f, "address ")?;
|
||||||
fmt::Display::fmt(&self.address.0, f)?;
|
fmt::Display::fmt(&self.address.inner(), f)?;
|
||||||
write!(f, " is not valid on {}", self.required)
|
write!(f, " is not valid on {}", self.required)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -404,7 +404,7 @@ struct DisplayUnchecked<'a, N: NetworkValidation>(&'a Address<N>);
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
impl<N: NetworkValidation> fmt::Display for DisplayUnchecked<'_, N> {
|
impl<N: NetworkValidation> fmt::Display for DisplayUnchecked<'_, N> {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0 .0, fmt) }
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0.inner(), fmt) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
@ -433,7 +433,7 @@ impl<'de, U: NetworkValidationUnchecked> serde::Deserialize<'de> for Address<U>
|
||||||
{
|
{
|
||||||
// We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
|
// We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
|
||||||
let address = v.parse::<Address<NetworkUnchecked>>().map_err(E::custom)?;
|
let address = v.parse::<Address<NetworkUnchecked>>().map_err(E::custom)?;
|
||||||
Ok(Address(address.0, PhantomData::<U>))
|
Ok(Address::from_inner(address.into_inner()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,18 +454,30 @@ impl<V: NetworkValidation> serde::Serialize for Address<V> {
|
||||||
/// Methods on [`Address`] that can be called on both `Address<NetworkChecked>` and
|
/// Methods on [`Address`] that can be called on both `Address<NetworkChecked>` and
|
||||||
/// `Address<NetworkUnchecked>`.
|
/// `Address<NetworkUnchecked>`.
|
||||||
impl<V: NetworkValidation> Address<V> {
|
impl<V: NetworkValidation> Address<V> {
|
||||||
|
fn from_inner(inner: AddressInner) -> Self {
|
||||||
|
Address(inner, PhantomData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner(self) -> AddressInner {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner(&self) -> &AddressInner {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a reference to the address as if it was unchecked.
|
/// Returns a reference to the address as if it was unchecked.
|
||||||
pub fn as_unchecked(&self) -> &Address<NetworkUnchecked> {
|
pub fn as_unchecked(&self) -> &Address<NetworkUnchecked> {
|
||||||
unsafe { &*(self as *const Address<V> as *const Address<NetworkUnchecked>) }
|
unsafe { &*(self as *const Address<V> as *const Address<NetworkUnchecked>) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks the network of this address as unchecked.
|
/// Marks the network of this address as unchecked.
|
||||||
pub fn into_unchecked(self) -> Address<NetworkUnchecked> { Address(self.0, PhantomData) }
|
pub fn into_unchecked(self) -> Address<NetworkUnchecked> { Address::from_inner(self.into_inner()) }
|
||||||
|
|
||||||
/// Returns the [`NetworkKind`] of this address.
|
/// Returns the [`NetworkKind`] of this address.
|
||||||
pub fn network_kind(&self) -> NetworkKind {
|
pub fn network_kind(&self) -> NetworkKind {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
match self.0 {
|
match *self.inner() {
|
||||||
P2pkh { hash: _, ref network } => *network,
|
P2pkh { hash: _, ref network } => *network,
|
||||||
P2sh { hash: _, ref network } => *network,
|
P2sh { hash: _, ref network } => *network,
|
||||||
Segwit { program: _, ref hrp } => NetworkKind::from(*hrp),
|
Segwit { program: _, ref hrp } => NetworkKind::from(*hrp),
|
||||||
|
@ -481,7 +493,7 @@ impl Address {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn p2pkh(pk: impl Into<PubkeyHash>, network: impl Into<NetworkKind>) -> Address {
|
pub fn p2pkh(pk: impl Into<PubkeyHash>, network: impl Into<NetworkKind>) -> Address {
|
||||||
let hash = pk.into();
|
let hash = pk.into();
|
||||||
Self(AddressInner::P2pkh { hash, network: network.into() }, PhantomData)
|
Self::from_inner(AddressInner::P2pkh { hash, network: network.into() })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new pay-to-script-hash (P2SH) [`Address`] from a script.
|
/// Constructs a new pay-to-script-hash (P2SH) [`Address`] from a script.
|
||||||
|
@ -504,7 +516,7 @@ impl Address {
|
||||||
/// The `hash` pre-image (redeem script) must not exceed 520 bytes in length
|
/// The `hash` pre-image (redeem script) must not exceed 520 bytes in length
|
||||||
/// otherwise outputs created from the returned address will be un-spendable.
|
/// otherwise outputs created from the returned address will be un-spendable.
|
||||||
pub fn p2sh_from_hash(hash: ScriptHash, network: impl Into<NetworkKind>) -> Address {
|
pub fn p2sh_from_hash(hash: ScriptHash, network: impl Into<NetworkKind>) -> Address {
|
||||||
Self(AddressInner::P2sh { hash, network: network.into() }, PhantomData)
|
Self::from_inner(AddressInner::P2sh { hash, network: network.into() })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new pay-to-witness-public-key-hash (P2WPKH) [`Address`] from a public key.
|
/// Constructs a new pay-to-witness-public-key-hash (P2WPKH) [`Address`] from a public key.
|
||||||
|
@ -577,7 +589,7 @@ impl Address {
|
||||||
/// then you likely do not need this constructor.
|
/// then you likely do not need this constructor.
|
||||||
pub fn from_witness_program(program: WitnessProgram, hrp: impl Into<KnownHrp>) -> Address {
|
pub fn from_witness_program(program: WitnessProgram, hrp: impl Into<KnownHrp>) -> Address {
|
||||||
let inner = AddressInner::Segwit { program, hrp: hrp.into() };
|
let inner = AddressInner::Segwit { program, hrp: hrp.into() };
|
||||||
Address(inner, PhantomData)
|
Address::from_inner(inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the address type of the [`Address`].
|
/// Gets the address type of the [`Address`].
|
||||||
|
@ -587,7 +599,7 @@ impl Address {
|
||||||
/// None if unknown, non-standard or related to the future witness version.
|
/// None if unknown, non-standard or related to the future witness version.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn address_type(&self) -> Option<AddressType> {
|
pub fn address_type(&self) -> Option<AddressType> {
|
||||||
match self.0 {
|
match *self.inner() {
|
||||||
AddressInner::P2pkh { .. } => Some(AddressType::P2pkh),
|
AddressInner::P2pkh { .. } => Some(AddressType::P2pkh),
|
||||||
AddressInner::P2sh { .. } => Some(AddressType::P2sh),
|
AddressInner::P2sh { .. } => Some(AddressType::P2sh),
|
||||||
AddressInner::Segwit { ref program, hrp: _ } =>
|
AddressInner::Segwit { ref program, hrp: _ } =>
|
||||||
|
@ -609,7 +621,7 @@ impl Address {
|
||||||
pub fn to_address_data(&self) -> AddressData {
|
pub fn to_address_data(&self) -> AddressData {
|
||||||
use AddressData::*;
|
use AddressData::*;
|
||||||
|
|
||||||
match self.0 {
|
match *self.inner() {
|
||||||
AddressInner::P2pkh { hash, network: _ } => P2pkh { pubkey_hash: hash },
|
AddressInner::P2pkh { hash, network: _ } => P2pkh { pubkey_hash: hash },
|
||||||
AddressInner::P2sh { hash, network: _ } => P2sh { script_hash: hash },
|
AddressInner::P2sh { hash, network: _ } => P2sh { script_hash: hash },
|
||||||
AddressInner::Segwit { program, hrp: _ } => Segwit { witness_program: program },
|
AddressInner::Segwit { program, hrp: _ } => Segwit { witness_program: program },
|
||||||
|
@ -620,7 +632,7 @@ impl Address {
|
||||||
pub fn pubkey_hash(&self) -> Option<PubkeyHash> {
|
pub fn pubkey_hash(&self) -> Option<PubkeyHash> {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
|
|
||||||
match self.0 {
|
match *self.inner() {
|
||||||
P2pkh { ref hash, network: _ } => Some(*hash),
|
P2pkh { ref hash, network: _ } => Some(*hash),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -630,7 +642,7 @@ impl Address {
|
||||||
pub fn script_hash(&self) -> Option<ScriptHash> {
|
pub fn script_hash(&self) -> Option<ScriptHash> {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
|
|
||||||
match self.0 {
|
match *self.inner() {
|
||||||
P2sh { ref hash, network: _ } => Some(*hash),
|
P2sh { ref hash, network: _ } => Some(*hash),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -640,7 +652,7 @@ impl Address {
|
||||||
pub fn witness_program(&self) -> Option<WitnessProgram> {
|
pub fn witness_program(&self) -> Option<WitnessProgram> {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
|
|
||||||
match self.0 {
|
match *self.inner() {
|
||||||
Segwit { ref program, hrp: _ } => Some(*program),
|
Segwit { ref program, hrp: _ } => Some(*program),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -689,7 +701,7 @@ impl Address {
|
||||||
/// Generates a script pubkey spending to this address.
|
/// Generates a script pubkey spending to this address.
|
||||||
pub fn script_pubkey(&self) -> ScriptBuf {
|
pub fn script_pubkey(&self) -> ScriptBuf {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
match self.0 {
|
match *self.inner() {
|
||||||
P2pkh { hash, network: _ } => ScriptBuf::new_p2pkh(hash),
|
P2pkh { hash, network: _ } => ScriptBuf::new_p2pkh(hash),
|
||||||
P2sh { hash, network: _ } => ScriptBuf::new_p2sh(hash),
|
P2sh { hash, network: _ } => ScriptBuf::new_p2sh(hash),
|
||||||
Segwit { ref program, hrp: _ } => {
|
Segwit { ref program, hrp: _ } => {
|
||||||
|
@ -756,7 +768,7 @@ impl Address {
|
||||||
/// This function doesn't make any allocations.
|
/// This function doesn't make any allocations.
|
||||||
pub fn matches_script_pubkey(&self, script: &Script) -> bool {
|
pub fn matches_script_pubkey(&self, script: &Script) -> bool {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
match self.0 {
|
match *self.inner() {
|
||||||
P2pkh { ref hash, network: _ } if script.is_p2pkh() =>
|
P2pkh { ref hash, network: _ } if script.is_p2pkh() =>
|
||||||
&script.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash),
|
&script.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash),
|
||||||
P2sh { ref hash, network: _ } if script.is_p2sh() =>
|
P2sh { ref hash, network: _ } if script.is_p2sh() =>
|
||||||
|
@ -777,7 +789,7 @@ impl Address {
|
||||||
/// - For SegWit addresses, the payload is the witness program.
|
/// - For SegWit addresses, the payload is the witness program.
|
||||||
fn payload_as_bytes(&self) -> &[u8] {
|
fn payload_as_bytes(&self) -> &[u8] {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
match self.0 {
|
match *self.inner() {
|
||||||
P2sh { ref hash, network: _ } => hash.as_ref(),
|
P2sh { ref hash, network: _ } => hash.as_ref(),
|
||||||
P2pkh { ref hash, network: _ } => hash.as_ref(),
|
P2pkh { ref hash, network: _ } => hash.as_ref(),
|
||||||
Segwit { ref program, hrp: _ } => program.program().as_bytes(),
|
Segwit { ref program, hrp: _ } => program.program().as_bytes(),
|
||||||
|
@ -817,7 +829,7 @@ impl Address<NetworkUnchecked> {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn is_valid_for_network(&self, n: Network) -> bool {
|
pub fn is_valid_for_network(&self, n: Network) -> bool {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
match self.0 {
|
match *self.inner() {
|
||||||
P2pkh { hash: _, ref network } => *network == NetworkKind::from(n),
|
P2pkh { hash: _, ref network } => *network == NetworkKind::from(n),
|
||||||
P2sh { hash: _, ref network } => *network == NetworkKind::from(n),
|
P2sh { hash: _, ref network } => *network == NetworkKind::from(n),
|
||||||
Segwit { program: _, ref hrp } => *hrp == KnownHrp::from_network(n),
|
Segwit { program: _, ref hrp } => *hrp == KnownHrp::from_network(n),
|
||||||
|
@ -882,7 +894,7 @@ impl Address<NetworkUnchecked> {
|
||||||
/// For details about this mechanism, see section [*Parsing addresses*](Address#parsing-addresses)
|
/// For details about this mechanism, see section [*Parsing addresses*](Address#parsing-addresses)
|
||||||
/// on [`Address`].
|
/// on [`Address`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn assume_checked(self) -> Address { Address(self.0, PhantomData) }
|
pub fn assume_checked(self) -> Address { Address::from_inner(self.into_inner()) }
|
||||||
|
|
||||||
/// Parse a bech32 Address string
|
/// Parse a bech32 Address string
|
||||||
pub fn from_bech32_str(s: &str) -> Result<Address<NetworkUnchecked>, Bech32Error> {
|
pub fn from_bech32_str(s: &str) -> Result<Address<NetworkUnchecked>, Bech32Error> {
|
||||||
|
@ -894,7 +906,7 @@ impl Address<NetworkUnchecked> {
|
||||||
|
|
||||||
let hrp = KnownHrp::from_hrp(hrp)?;
|
let hrp = KnownHrp::from_hrp(hrp)?;
|
||||||
let inner = AddressInner::Segwit { program, hrp };
|
let inner = AddressInner::Segwit { program, hrp };
|
||||||
Ok(Address(inner, PhantomData))
|
Ok(Address::from_inner(inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a base58 Address string
|
/// Parse a base58 Address string
|
||||||
|
@ -927,7 +939,7 @@ impl Address<NetworkUnchecked> {
|
||||||
invalid => return Err(InvalidLegacyPrefixError { invalid }.into()),
|
invalid => return Err(InvalidLegacyPrefixError { invalid }.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Address(inner, PhantomData))
|
Ok(Address::from_inner(inner))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -938,16 +950,16 @@ impl From<Address> for ScriptBuf {
|
||||||
// Alternate formatting `{:#}` is used to return uppercase version of bech32 addresses which should
|
// Alternate formatting `{:#}` is used to return uppercase version of bech32 addresses which should
|
||||||
// be used in QR codes, see [`Address::to_qr_uri`].
|
// be used in QR codes, see [`Address::to_qr_uri`].
|
||||||
impl fmt::Display for Address {
|
impl fmt::Display for Address {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, fmt) }
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.inner(), fmt) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: NetworkValidation> fmt::Debug for Address<V> {
|
impl<V: NetworkValidation> fmt::Debug for Address<V> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
if V::IS_CHECKED {
|
if V::IS_CHECKED {
|
||||||
fmt::Display::fmt(&self.0, f)
|
fmt::Display::fmt(&self.inner(), f)
|
||||||
} else {
|
} else {
|
||||||
write!(f, "Address<NetworkUnchecked>(")?;
|
write!(f, "Address<NetworkUnchecked>(")?;
|
||||||
fmt::Display::fmt(&self.0, f)?;
|
fmt::Display::fmt(&self.inner(), f)?;
|
||||||
write!(f, ")")
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -975,10 +987,10 @@ impl<U: NetworkValidationUnchecked> FromStr for Address<U> {
|
||||||
if ["bc1", "bcrt1", "tb1"].iter().any(|&prefix| s.to_lowercase().starts_with(prefix)) {
|
if ["bc1", "bcrt1", "tb1"].iter().any(|&prefix| s.to_lowercase().starts_with(prefix)) {
|
||||||
let address = Address::from_bech32_str(s)?;
|
let address = Address::from_bech32_str(s)?;
|
||||||
// We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
|
// We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
|
||||||
Ok(Address(address.0, PhantomData::<U>))
|
Ok(Address::from_inner(address.into_inner()))
|
||||||
} else if ["1", "2", "3", "m", "n"].iter().any(|&prefix| s.starts_with(prefix)) {
|
} else if ["1", "2", "3", "m", "n"].iter().any(|&prefix| s.starts_with(prefix)) {
|
||||||
let address = Address::from_base58_str(s)?;
|
let address = Address::from_base58_str(s)?;
|
||||||
Ok(Address(address.0, PhantomData::<U>))
|
Ok(Address::from_inner(address.into_inner()))
|
||||||
} else {
|
} else {
|
||||||
let hrp = match s.rfind('1') {
|
let hrp = match s.rfind('1') {
|
||||||
Some(pos) => &s[..pos],
|
Some(pos) => &s[..pos],
|
||||||
|
|
Loading…
Reference in New Issue