use core::convert::{TryFrom, TryInto};
use core::fmt;
use core::marker::PhantomData;
use core::str::FromStr;
use bech32::primitives::hrp::{self, Hrp};
use hashes::{sha256, Hash, HashEngine};
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
use crate::base58;
use crate::blockdata::constants::{
MAX_SCRIPT_ELEMENT_SIZE, PUBKEY_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST,
SCRIPT_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_TEST,
};
use crate::blockdata::script::witness_program::WitnessProgram;
use crate::blockdata::script::witness_version::WitnessVersion;
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;
pub mod error;
pub use self::error::{Error, ParseError, UnknownAddressTypeError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum AddressType {
P2pkh,
P2sh,
P2wpkh,
P2wsh,
P2tr,
}
impl fmt::Display for AddressType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
AddressType::P2pkh => "p2pkh",
AddressType::P2sh => "p2sh",
AddressType::P2wpkh => "p2wpkh",
AddressType::P2wsh => "p2wsh",
AddressType::P2tr => "p2tr",
})
}
}
impl FromStr for AddressType {
type Err = UnknownAddressTypeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"p2pkh" => Ok(AddressType::P2pkh),
"p2sh" => Ok(AddressType::P2sh),
"p2wpkh" => Ok(AddressType::P2wpkh),
"p2wsh" => Ok(AddressType::P2wsh),
"p2tr" => Ok(AddressType::P2tr),
_ => Err(UnknownAddressTypeError(s.to_owned())),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum Payload {
PubkeyHash(PubkeyHash),
ScriptHash(ScriptHash),
WitnessProgram(WitnessProgram),
}
impl Payload {
pub fn from_script(script: &Script) -> Result<Payload, Error> {
Ok(if script.is_p2pkh() {
let bytes = script.as_bytes()[3..23].try_into().expect("statically 20B long");
Payload::PubkeyHash(PubkeyHash::from_byte_array(bytes))
} else if script.is_p2sh() {
let bytes = script.as_bytes()[2..22].try_into().expect("statically 20B long");
Payload::ScriptHash(ScriptHash::from_byte_array(bytes))
} else if script.is_witness_program() {
let opcode = script.first_opcode().expect("witness_version guarantees len() > 4");
let witness_program = script.as_bytes()[2..].to_vec();
let witness_program =
WitnessProgram::new(WitnessVersion::try_from(opcode)?, witness_program)?;
Payload::WitnessProgram(witness_program)
} else {
return Err(Error::UnrecognizedScript);
})
}
pub fn script_pubkey(&self) -> ScriptBuf {
match *self {
Payload::PubkeyHash(ref hash) => ScriptBuf::new_p2pkh(hash),
Payload::ScriptHash(ref hash) => ScriptBuf::new_p2sh(hash),
Payload::WitnessProgram(ref prog) => ScriptBuf::new_witness_program(prog),
}
}
pub fn matches_script_pubkey(&self, script: &Script) -> bool {
match *self {
Payload::PubkeyHash(ref hash) if script.is_p2pkh() => {
&script.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash)
}
Payload::ScriptHash(ref hash) if script.is_p2sh() => {
&script.as_bytes()[2..22] == <ScriptHash as AsRef<[u8; 20]>>::as_ref(hash)
}
Payload::WitnessProgram(ref prog) if script.is_witness_program() => {
&script.as_bytes()[2..] == prog.program().as_bytes()
}
Payload::PubkeyHash(_) | Payload::ScriptHash(_) | Payload::WitnessProgram(_) => false,
}
}
#[inline]
pub fn p2pkh(pk: &PublicKey) -> Payload {
Payload::PubkeyHash(pk.pubkey_hash())
}
#[inline]
pub fn p2sh(script: &Script) -> Result<Payload, Error> {
if script.len() > MAX_SCRIPT_ELEMENT_SIZE {
return Err(Error::ExcessiveScriptSize);
}
Ok(Payload::ScriptHash(script.script_hash()))
}
pub fn p2wpkh(pk: &PublicKey) -> Result<Payload, Error> {
let prog = WitnessProgram::new(
WitnessVersion::V0,
pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?,
)?;
Ok(Payload::WitnessProgram(prog))
}
pub fn p2shwpkh(pk: &PublicKey) -> Result<Payload, Error> {
let builder = script::Builder::new()
.push_int(0)
.push_slice(pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?);
Ok(Payload::ScriptHash(builder.into_script().script_hash()))
}
pub fn p2wsh(script: &Script) -> Payload {
let prog = WitnessProgram::new(WitnessVersion::V0, script.wscript_hash())
.expect("wscript_hash has len 32 compatible with segwitv0");
Payload::WitnessProgram(prog)
}
pub fn p2shwsh(script: &Script) -> Payload {
let ws = script::Builder::new().push_int(0).push_slice(script.wscript_hash()).into_script();
Payload::ScriptHash(ws.script_hash())
}
pub fn p2tr<C: Verification>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
merkle_root: Option<TapNodeHash>,
) -> Payload {
let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root);
let prog = WitnessProgram::new(WitnessVersion::V1, output_key.to_inner().serialize())
.expect("taproot output key has len 32 <= 40");
Payload::WitnessProgram(prog)
}
pub fn p2tr_tweaked(output_key: TweakedPublicKey) -> Payload {
let prog = WitnessProgram::new(WitnessVersion::V1, output_key.to_inner().serialize())
.expect("taproot output key has len 32 <= 40");
Payload::WitnessProgram(prog)
}
fn inner_prog_as_bytes(&self) -> &[u8] {
match self {
Payload::ScriptHash(hash) => hash.as_ref(),
Payload::PubkeyHash(hash) => hash.as_ref(),
Payload::WitnessProgram(prog) => prog.program().as_bytes(),
}
}
}
pub struct AddressEncoding<'a> {
pub payload: &'a Payload,
pub p2pkh_prefix: u8,
pub p2sh_prefix: u8,
pub hrp: Hrp,
}
impl fmt::Display for AddressEncoding<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.payload {
Payload::PubkeyHash(hash) => {
let mut prefixed = [0; 21];
prefixed[0] = self.p2pkh_prefix;
prefixed[1..].copy_from_slice(&hash[..]);
base58::encode_check_to_fmt(fmt, &prefixed[..])
}
Payload::ScriptHash(hash) => {
let mut prefixed = [0; 21];
prefixed[0] = self.p2sh_prefix;
prefixed[1..].copy_from_slice(&hash[..]);
base58::encode_check_to_fmt(fmt, &prefixed[..])
}
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_upper_to_fmt_unchecked(fmt, hrp, version, program)
} else {
bech32::segwit::encode_lower_to_fmt_unchecked(fmt, hrp, version, program)
}
}
}
}
}
mod sealed {
pub trait NetworkValidation {}
impl NetworkValidation for super::NetworkChecked {}
impl NetworkValidation for super::NetworkUnchecked {}
}
pub trait NetworkValidation: sealed::NetworkValidation + Sync + Send + Sized + Unpin {
const IS_CHECKED: bool;
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NetworkChecked {}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NetworkUnchecked {}
impl NetworkValidation for NetworkChecked {
const IS_CHECKED: bool = true;
}
impl NetworkValidation for NetworkUnchecked {
const IS_CHECKED: bool = false;
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct AddressInner {
payload: Payload,
network: Network,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Address<V = NetworkChecked>(AddressInner, PhantomData<V>)
where
V: NetworkValidation;
#[cfg(feature = "serde")]
struct DisplayUnchecked<'a, N: NetworkValidation>(&'a Address<N>);
#[cfg(feature = "serde")]
impl<N: NetworkValidation> fmt::Display for DisplayUnchecked<'_, N> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_internal(fmt)
}
}
#[cfg(feature = "serde")]
crate::serde_utils::serde_string_deserialize_impl!(Address<NetworkUnchecked>, "a Bitcoin address");
#[cfg(feature = "serde")]
impl<N: NetworkValidation> serde::Serialize for Address<N> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_str(&DisplayUnchecked(self))
}
}
impl<V: NetworkValidation> Address<V> {
pub fn payload(&self) -> &Payload {
&self.0.payload
}
pub fn network(&self) -> &Network {
&self.0.network
}
pub fn as_unchecked(&self) -> &Address<NetworkUnchecked> {
unsafe { &*(self as *const Address<V> as *const Address<NetworkUnchecked>) }
}
pub fn into_parts(self) -> (Network, Payload) {
let AddressInner { payload, network } = self.0;
(network, payload)
}
fn address_type_internal(&self) -> Option<AddressType> {
match self.payload() {
Payload::PubkeyHash(_) => Some(AddressType::P2pkh),
Payload::ScriptHash(_) => Some(AddressType::P2sh),
Payload::WitnessProgram(ref prog) => {
match prog.version() {
WitnessVersion::V0 => match prog.program().len() {
20 => Some(AddressType::P2wpkh),
32 => Some(AddressType::P2wsh),
_ => unreachable!(
"Address creation invariant violation: invalid program length"
),
},
WitnessVersion::V1 if prog.program().len() == 32 => Some(AddressType::P2tr),
_ => None,
}
}
}
}
fn fmt_internal(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let p2pkh_prefix = match self.network() {
Network::Mainnet => PUBKEY_ADDRESS_PREFIX_MAIN,
Network::Testnet | Network::Signet | Network::Regtest => PUBKEY_ADDRESS_PREFIX_TEST,
};
let p2sh_prefix = match self.network() {
Network::Mainnet => SCRIPT_ADDRESS_PREFIX_MAIN,
Network::Testnet | Network::Signet | Network::Regtest => SCRIPT_ADDRESS_PREFIX_TEST,
};
let hrp = match self.network() {
Network::Mainnet => hrp::BC,
Network::Testnet | Network::Signet => hrp::TB,
Network::Regtest => hrp::BCRT,
};
let encoding = AddressEncoding { payload: self.payload(), p2pkh_prefix, p2sh_prefix, hrp };
use fmt::Display;
encoding.fmt(fmt)
}
#[inline]
pub fn new(network: Network, payload: Payload) -> Self {
Self(AddressInner { network, payload }, PhantomData)
}
}
impl Address {
#[inline]
pub fn p2pkh(pk: &PublicKey, network: Network) -> Address {
Address::new(network, Payload::p2pkh(pk))
}
#[inline]
pub fn p2sh(script: &Script, network: Network) -> Result<Address, Error> {
Ok(Address::new(network, Payload::p2sh(script)?))
}
pub fn p2wpkh(pk: &PublicKey, network: Network) -> Result<Address, Error> {
Ok(Address::new(network, Payload::p2wpkh(pk)?))
}
pub fn p2shwpkh(pk: &PublicKey, network: Network) -> Result<Address, Error> {
Ok(Address::new(network, Payload::p2shwpkh(pk)?))
}
pub fn p2wsh(script: &Script, network: Network) -> Address {
Address::new(network, Payload::p2wsh(script))
}
pub fn p2shwsh(script: &Script, network: Network) -> Address {
Address::new(network, Payload::p2shwsh(script))
}
pub fn p2tr<C: Verification>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
merkle_root: Option<TapNodeHash>,
network: Network,
) -> Address {
Address::new(network, Payload::p2tr(secp, internal_key, merkle_root))
}
pub fn p2tr_tweaked(output_key: TweakedPublicKey, network: Network) -> Address {
Address::new(network, Payload::p2tr_tweaked(output_key))
}
#[inline]
pub fn address_type(&self) -> Option<AddressType> {
self.address_type_internal()
}
pub fn is_spend_standard(&self) -> bool {
self.address_type().is_some()
}
pub fn from_script(script: &Script, network: Network) -> Result<Address, Error> {
Ok(Address::new(network, Payload::from_script(script)?))
}
pub fn script_pubkey(&self) -> ScriptBuf {
self.payload().script_pubkey()
}
pub fn to_qr_uri(&self) -> String {
format!("bellscoin:{:#}", self)
}
pub fn is_related_to_pubkey(&self, pubkey: &PublicKey) -> bool {
let pubkey_hash = pubkey.pubkey_hash();
let payload = self.payload().inner_prog_as_bytes();
let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner);
(*pubkey_hash.as_byte_array() == *payload)
|| (xonly_pubkey.serialize() == *payload)
|| (*segwit_redeem_hash(&pubkey_hash).as_byte_array() == *payload)
}
pub fn is_related_to_xonly_pubkey(&self, xonly_pubkey: &XOnlyPublicKey) -> bool {
let payload = self.payload().inner_prog_as_bytes();
payload == xonly_pubkey.serialize()
}
pub fn matches_script_pubkey(&self, script_pubkey: &Script) -> bool {
self.payload().matches_script_pubkey(script_pubkey)
}
}
impl Address<NetworkUnchecked> {
pub fn assume_checked_ref(&self) -> &Address {
unsafe { &*(self as *const Address<NetworkUnchecked> as *const Address) }
}
pub fn is_valid_for_network(&self, network: Network) -> bool {
let is_legacy = matches!(
self.address_type_internal(),
Some(AddressType::P2pkh) | Some(AddressType::P2sh)
);
match (self.network(), network) {
(a, b) if *a == b => true,
(Network::Mainnet, _) | (_, Network::Mainnet) => false,
(Network::Regtest, _) | (_, Network::Regtest) if !is_legacy => false,
(Network::Testnet, _) | (Network::Regtest, _) | (Network::Signet, _) => true,
}
}
#[inline]
pub fn require_network(self, required: Network) -> Result<Address, Error> {
if self.is_valid_for_network(required) {
Ok(self.assume_checked())
} else {
Err(Error::NetworkValidation { found: *self.network(), required, address: self })
}
}
#[inline]
pub fn assume_checked(self) -> Address {
let (network, payload) = self.into_parts();
Address::new(network, payload)
}
}
impl PartialEq<Address<NetworkUnchecked>> for Address {
fn eq(&self, other: &Address<NetworkUnchecked>) -> bool {
self.network() == other.network() && self.payload() == other.payload()
}
}
impl PartialEq<Address> for Address<NetworkUnchecked> {
fn eq(&self, other: &Address) -> bool {
other == self
}
}
impl From<Address> for script::ScriptBuf {
fn from(a: Address) -> Self {
a.script_pubkey()
}
}
impl fmt::Display for Address {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.fmt_internal(fmt)
}
}
impl<V: NetworkValidation> fmt::Debug for Address<V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if V::IS_CHECKED {
self.fmt_internal(f)
} else {
write!(f, "Address<NetworkUnchecked>(")?;
self.fmt_internal(f)?;
write!(f, ")")
}
}
}
fn find_bech32_prefix(bech32: &str) -> &str {
match bech32.rfind('1') {
None => bech32,
Some(sep) => bech32.split_at(sep).0,
}
}
impl FromStr for Address<NetworkUnchecked> {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let bech32_network = match find_bech32_prefix(s) {
"bel" => Some(Network::Mainnet),
"tbel" => Some(Network::Testnet), "bcrt" => Some(Network::Regtest),
_ => None,
};
if let Some(network) = bech32_network {
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)?;
return Ok(Address::new(network, Payload::WitnessProgram(witness_program)));
}
if s.len() > 50 {
return Err(ParseError::Base58(base58::Error::InvalidLength(s.len() * 11 / 15)));
}
let data = base58::decode_check(s)?;
if data.len() != 21 {
return Err(ParseError::Base58(base58::Error::InvalidLength(data.len())));
}
let (network, payload) = match data[0] {
PUBKEY_ADDRESS_PREFIX_MAIN => {
(Network::Mainnet, Payload::PubkeyHash(PubkeyHash::from_slice(&data[1..]).unwrap()))
}
SCRIPT_ADDRESS_PREFIX_MAIN => {
(Network::Mainnet, Payload::ScriptHash(ScriptHash::from_slice(&data[1..]).unwrap()))
}
PUBKEY_ADDRESS_PREFIX_TEST => {
(Network::Testnet, Payload::PubkeyHash(PubkeyHash::from_slice(&data[1..]).unwrap()))
}
SCRIPT_ADDRESS_PREFIX_TEST => {
(Network::Testnet, Payload::ScriptHash(ScriptHash::from_slice(&data[1..]).unwrap()))
}
x => return Err(ParseError::Base58(base58::Error::InvalidAddressVersion(x))),
};
Ok(Address::new(network, payload))
}
}
fn segwit_redeem_hash(pubkey_hash: &PubkeyHash) -> crate::hashes::hash160::Hash {
let mut sha_engine = sha256::Hash::engine();
sha_engine.input(&[0, 20]);
sha_engine.input(pubkey_hash.as_ref());
crate::hashes::hash160::Hash::from_engine(sha_engine)
}