diff --git a/stackslib/src/burnchains/bitcoin/address.rs b/stackslib/src/burnchains/bitcoin/address.rs index 1b7a63cbcf..519c925e88 100644 --- a/stackslib/src/burnchains/bitcoin/address.rs +++ b/stackslib/src/burnchains/bitcoin/address.rs @@ -45,13 +45,13 @@ pub struct LegacyBitcoinAddress { pub bytes: Hash160, } -/// Segwit address. The bool member is "mainnet" (to determine the hrp) +/// Segwit address. The [`BitcoinNetworkType`] member allows to to determine the HRP /// New in 2.1 #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub enum SegwitBitcoinAddress { - P2WPKH(bool, [u8; 20]), - P2WSH(bool, [u8; 32]), - P2TR(bool, [u8; 32]), + P2WPKH(BitcoinNetworkType, [u8; 20]), + P2WSH(BitcoinNetworkType, [u8; 32]), + P2TR(BitcoinNetworkType, [u8; 32]), } #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] @@ -81,6 +81,7 @@ pub const ADDRESS_VERSION_TESTNET_MULTISIG: u8 = 196; // segwit hrps pub const SEGWIT_MAINNET_HRP: &str = "bc"; pub const SEGWIT_TESTNET_HRP: &str = "tb"; +pub const SEGWIT_REGTEST_HRP: &str = "bcrt"; // segwit witnes versions pub const SEGWIT_V0: u8 = 0; @@ -155,15 +156,6 @@ pub fn to_b58_version_byte(version: u8) -> Option { } } -/// Get the HRP for a segwit address based on whether or not we're in mainnet -pub fn segwit_hrp(mainnet: bool) -> &'static str { - if mainnet { - SEGWIT_MAINNET_HRP - } else { - SEGWIT_TESTNET_HRP - } -} - impl LegacyBitcoinAddress { fn to_versioned_bytes(&self) -> [u8; 21] { let mut ret = [0; 21]; @@ -269,11 +261,26 @@ impl SegwitBitcoinAddress { version_bytes } + /// Returns `true` if this Segwit address belongs to the Mainnet network. pub fn is_mainnet(&self) -> bool { + self.network().is_mainnet() + } + + /// Returns the Bitcoin network type associated with this Segwit address. + pub fn network(&self) -> BitcoinNetworkType { match *self { - SegwitBitcoinAddress::P2WPKH(ref mainnet, _) => *mainnet, - SegwitBitcoinAddress::P2WSH(ref mainnet, _) => *mainnet, - SegwitBitcoinAddress::P2TR(ref mainnet, _) => *mainnet, + SegwitBitcoinAddress::P2WPKH(ref network, _) + | SegwitBitcoinAddress::P2WSH(ref network, _) + | SegwitBitcoinAddress::P2TR(ref network, _) => *network, + } + } + + /// Returns the HRP string associated with address network + pub fn hrp(&self) -> &'static str { + match self.network() { + BitcoinNetworkType::Mainnet => SEGWIT_MAINNET_HRP, + BitcoinNetworkType::Testnet => SEGWIT_TESTNET_HRP, + BitcoinNetworkType::Regtest => SEGWIT_REGTEST_HRP, } } @@ -295,8 +302,7 @@ impl SegwitBitcoinAddress { } pub fn to_bech32(&self) -> String { - let hrp = segwit_hrp(self.is_mainnet()); - self.to_bech32_hrp(hrp) + self.to_bech32_hrp(self.hrp()) } pub fn from_bech32(s: &str) -> Option { @@ -306,14 +312,15 @@ impl SegwitBitcoinAddress { }) .ok()?; - let mainnet = if hrp == SEGWIT_MAINNET_HRP { - Some(true) - } else if hrp == SEGWIT_TESTNET_HRP { - Some(false) - } else { - test_debug!("Unrecognized hrp '{:?}'", &hrp); - None - }?; + let network_type = match hrp.as_str() { + SEGWIT_MAINNET_HRP => BitcoinNetworkType::Mainnet, + SEGWIT_TESTNET_HRP => BitcoinNetworkType::Testnet, + SEGWIT_REGTEST_HRP => BitcoinNetworkType::Regtest, + _ => { + test_debug!("Unrecognized hrp '{:?}'", &hrp); + return None; + } + }; if quintets.is_empty() || quintets.len() > 65 { test_debug!("Invalid prog length: {}", quintets.len()); @@ -333,15 +340,15 @@ impl SegwitBitcoinAddress { match (variant, version, bytes.len()) { (bech32::Variant::Bech32, SEGWIT_V0, 20) => { let bytes_20 = bytes.try_into().ok()?; - Some(SegwitBitcoinAddress::P2WPKH(mainnet, bytes_20)) + Some(SegwitBitcoinAddress::P2WPKH(network_type, bytes_20)) } (bech32::Variant::Bech32, SEGWIT_V0, 32) => { let bytes_32 = bytes.try_into().ok()?; - Some(SegwitBitcoinAddress::P2WSH(mainnet, bytes_32)) + Some(SegwitBitcoinAddress::P2WSH(network_type, bytes_32)) } (bech32::Variant::Bech32m, SEGWIT_V1, 32) => { let bytes_32 = bytes.try_into().ok()?; - Some(SegwitBitcoinAddress::P2TR(mainnet, bytes_32)) + Some(SegwitBitcoinAddress::P2TR(network_type, bytes_32)) } (_, _, _) => { test_debug!( @@ -437,9 +444,8 @@ impl BitcoinAddress { .try_into() .map_err(|_| btc_error::InvalidByteSequence)?; - let mainnet = network_id == BitcoinNetworkType::Mainnet; Ok(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH( - mainnet, *my_bytes, + network_id, *my_bytes, ))) } @@ -480,9 +486,8 @@ impl BitcoinAddress { // segwit p2wpkh let witness_program: &[u8; 20] = scriptpubkey.get(2..22)?.try_into().ok()?; - let mainnet = network_id == BitcoinNetworkType::Mainnet; Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH( - mainnet, + network_id, *witness_program, ))) } else if scriptpubkey.len() == 34 @@ -491,9 +496,8 @@ impl BitcoinAddress { // segwit p2wsh let witness_program: &[u8; 32] = scriptpubkey.get(2..34)?.try_into().ok()?; - let mainnet = network_id == BitcoinNetworkType::Mainnet; Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH( - mainnet, + network_id, *witness_program, ))) } else if scriptpubkey.len() == 34 @@ -502,9 +506,8 @@ impl BitcoinAddress { // segwit p2tr let witness_program: &[u8; 32] = scriptpubkey.get(2..34)?.try_into().ok()?; - let mainnet = network_id == BitcoinNetworkType::Mainnet; Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR( - mainnet, + network_id, *witness_program, ))) } else { @@ -673,26 +676,58 @@ impl Address for BitcoinAddress { #[cfg(test)] mod tests { + use stacks_common::deps_common::bech32::Variant; use stacks_common::types::Address; use stacks_common::util::hash::{hex_bytes, Hash160}; - use super::{ - BitcoinAddress, LegacyBitcoinAddress, LegacyBitcoinAddressType, SegwitBitcoinAddress, - }; + use super::*; use crate::burnchains::bitcoin::BitcoinNetworkType; - struct AddressFixture { - addr: String, - result: Option, - } + pub mod utils { + use super::*; + + // Mainnet addresses + pub const MAINNET_ADDR_LEGACY_P2PKH: &str = "1DwGAhJLhTi53QN6v6dVafgHJBXKXJQ6Uw"; + pub const MAINNET_ADDR_LEGACY_P2SH: &str = "3EdH6EnnFN2T8a4Y3CJ61J3DShp36uuHxd"; + pub const MAINNET_ADDR_BECH32_P2WPKH: &str = "bc1q3hj2rk4fxj6wewmj63tzx4qadhxd39e449ft4k"; + pub const MAINNET_ADDR_BECH32_P2WSH: &str = + "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3"; + pub const MAINNET_ADDR_BECH32M_P2TR: &str = + "bc1p8vg588hldsnv4a558apet4e9ff3pr4awhqj2hy8gy6x2yxzjpmqsvvpta4"; + + // Testnet addresses + pub const TESTNET_ADDR_LEGACY_P2PKH: &str = "mr6QvHWqM7x6aR2ZubM5nwzLyqK2v8AmEr"; + pub const TESTNET_ADDR_LEGACY_P2SH: &str = "2N3pgcWrKhTLa6FsvzFfB1c8DUiDDNefjMH"; + pub const TESTNET_ADDR_BECH32_P2WPKH: &str = "tb1qkgm3dcvrhgy5n32adjkzrglfg9mwa5gjmwt5ex"; + pub const TESTNET_ADDR_BECH32_P2WSH: &str = + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"; + pub const TESTNET_ADDR_BECH32M_P2TR: &str = + "tb1p4tp4l6glyr2gs94neqcpr5gha7344nfyznfkc8szkreflscsdkgqsdent4"; + + // Regtest addresses (legacy addresses have same format as testent, but added for completness) + pub const REGTEST_ADDR_LEGACY_P2PKH: &str = "mrEXpzVbpgFzZ8cYBWzpWeMjFAnv7tJnzY"; + pub const REGTEST_ADDR_LEGACY_P2SH: &str = "2NGMuPBTF6uWQyTaevzdus5i6E1AdECXPhC"; + pub const REGTEST_ADDR_BECH32_P2WPKH: &str = "bcrt1qtsszg5k8dscmegzz048xpsceu2fjmtarkp6wjm"; + pub const REGTEST_ADDR_BECH32_P2WSH: &str = + "bcrt1q90a20y4ypm589jv6j66p9y4p979p0dh33mw50a3vefpmywpzu8wq42yz6v"; + pub const REGTEST_ADDR_BECH32M_P2TR: &str = + "bcrt1p4nsxnhzqhx3yvjp8zy74ggljdy8zllsnf37jf70mlqelvc3ad4aq3ug5ay"; + + pub struct AddressFixture { + pub addr: String, + pub result: Option, + } - struct ScriptFixture { - scriptpubkey: Vec, - result: Option, + pub struct ScriptFixture { + pub scriptpubkey: Vec, + pub result: Option, + } } #[test] fn test_from_b58() { + use utils::AddressFixture; + let fixtures = vec![ AddressFixture { addr: "mr6nrMvvh44sR5MiX929mMXP5hqgaTr6fx".to_owned(), @@ -766,12 +801,14 @@ mod tests { #[test] fn test_from_bech32() { + use utils::AddressFixture; + let fixtures = vec![ // taken from bip-0173 AddressFixture { addr: "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4".to_owned(), result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH( - true, + BitcoinNetworkType::Mainnet, [ 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, @@ -781,7 +818,7 @@ mod tests { AddressFixture { addr: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7".to_owned(), result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH( - false, + BitcoinNetworkType::Testnet, [ 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19, 0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1, @@ -808,7 +845,7 @@ mod tests { AddressFixture { addr: "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy".to_owned(), result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH( - false, + BitcoinNetworkType::Testnet, [ 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1, 0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2, @@ -894,6 +931,8 @@ mod tests { #[test] fn test_from_scriptpubkey() { + use utils::ScriptFixture; + let fixtures = vec![ ScriptFixture { scriptpubkey: hex_bytes("76a9146ea17fc39169cdd9f2414a893aa5ce0c4b4c893488ac") @@ -942,7 +981,7 @@ mod tests { .unwrap() .to_vec(), result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH( - true, + BitcoinNetworkType::Mainnet, [ 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, @@ -957,7 +996,7 @@ mod tests { .unwrap() .to_vec(), result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH( - true, + BitcoinNetworkType::Mainnet, [ 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19, 0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1, @@ -973,7 +1012,7 @@ mod tests { .unwrap() .to_vec(), result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH( - true, + BitcoinNetworkType::Mainnet, [ 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1, 0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2, @@ -990,7 +1029,7 @@ mod tests { .unwrap() .to_vec(), result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR( - true, + BitcoinNetworkType::Mainnet, [ 0x33, 0x9c, 0xe7, 0xe1, 0x65, 0xe6, 0x7d, 0x93, 0xad, 0xb3, 0xfe, 0xf8, 0x8a, 0x6d, 0x4b, 0xee, 0xd3, 0x3f, 0x01, 0xfa, 0x87, 0x6f, 0x05, 0xa2, @@ -1072,4 +1111,169 @@ mod tests { } } } + + #[test] + fn test_from_bech32_p2wpkh_string_regtest() { + let addr_str = utils::REGTEST_ADDR_BECH32_P2WPKH; + + let addr = SegwitBitcoinAddress::from_bech32(addr_str).unwrap(); + assert_eq!(addr_str, addr.to_bech32(), "to bench32 check"); + assert_eq!(addr_str, addr.to_string(), "to string check"); + assert_eq!(Variant::Bech32, addr.bech32_variant(), "variant check"); + assert_eq!(true, addr.is_p2wpkh(), "type check"); + assert_eq!(false, addr.is_mainnet(), "mainnet check"); + assert_eq!(SEGWIT_REGTEST_HRP, addr.hrp(), "hrp check"); + assert_eq!(BitcoinNetworkType::Regtest, addr.network(), "network check"); + } + + #[test] + fn test_from_bech32_p2swh_string_regtest() { + let addr_str = utils::REGTEST_ADDR_BECH32_P2WSH; + + let addr = SegwitBitcoinAddress::from_bech32(addr_str).unwrap(); + assert_eq!(addr_str, addr.to_bech32(), "to bench32 check"); + assert_eq!(addr_str, addr.to_string(), "to string check"); + assert_eq!(Variant::Bech32, addr.bech32_variant(), "variant check"); + assert_eq!(true, addr.is_p2wsh(), "type check"); + assert_eq!(false, addr.is_mainnet(), "mainnet check"); + assert_eq!(SEGWIT_REGTEST_HRP, addr.hrp(), "hrp check"); + assert_eq!(BitcoinNetworkType::Regtest, addr.network(), "network check"); + } + + #[test] + fn test_from_bech32m_p2tr_string_regtest() { + let addr_str = utils::REGTEST_ADDR_BECH32M_P2TR; + + let addr = SegwitBitcoinAddress::from_bech32(addr_str).unwrap(); + assert_eq!(addr_str, addr.to_bech32(), "to bench32 check"); + assert_eq!(addr_str, addr.to_string(), "to string check"); + assert_eq!(Variant::Bech32m, addr.bech32_variant(), "variant check"); + assert_eq!(false, addr.is_mainnet(), "mainnet check"); + assert_eq!(true, addr.is_p2tr(), "type check"); + assert_eq!(SEGWIT_REGTEST_HRP, addr.hrp(), "hrp check"); + assert_eq!(BitcoinNetworkType::Regtest, addr.network(), "network check"); + } + + #[test] + fn test_from_string_mainnet() { + let legacy_p2pkh = utils::MAINNET_ADDR_LEGACY_P2PKH; + let legacy_p2sh = utils::MAINNET_ADDR_LEGACY_P2SH; + let bech32_p2wpkh = utils::MAINNET_ADDR_BECH32_P2WPKH; + let bech32_p2wsh = utils::MAINNET_ADDR_BECH32_P2WSH; + let bech32m_p2tr = utils::MAINNET_ADDR_BECH32M_P2TR; + + assert_eq!( + legacy_p2pkh, + BitcoinAddress::from_string(legacy_p2pkh) + .unwrap() + .to_string() + ); + assert_eq!( + legacy_p2sh, + BitcoinAddress::from_string(legacy_p2sh) + .unwrap() + .to_string() + ); + assert_eq!( + bech32_p2wpkh, + BitcoinAddress::from_string(bech32_p2wpkh) + .unwrap() + .to_string() + ); + assert_eq!( + bech32_p2wsh, + BitcoinAddress::from_string(bech32_p2wsh) + .unwrap() + .to_string() + ); + assert_eq!( + bech32m_p2tr, + BitcoinAddress::from_string(bech32m_p2tr) + .unwrap() + .to_string() + ); + } + + #[test] + fn test_from_string_testnet() { + let legacy_p2pkh = utils::TESTNET_ADDR_LEGACY_P2PKH; + let legacy_p2sh = utils::TESTNET_ADDR_LEGACY_P2SH; + let bech32_p2wpkh = utils::TESTNET_ADDR_BECH32_P2WPKH; + let bech32_p2wsh = utils::TESTNET_ADDR_BECH32_P2WSH; + let bech32m_p2tr = utils::TESTNET_ADDR_BECH32M_P2TR; + + assert_eq!( + legacy_p2pkh, + BitcoinAddress::from_string(legacy_p2pkh) + .unwrap() + .to_string() + ); + assert_eq!( + legacy_p2sh, + BitcoinAddress::from_string(legacy_p2sh) + .unwrap() + .to_string() + ); + assert_eq!( + bech32_p2wpkh, + BitcoinAddress::from_string(bech32_p2wpkh) + .unwrap() + .to_string() + ); + assert_eq!( + bech32_p2wsh, + BitcoinAddress::from_string(bech32_p2wsh) + .unwrap() + .to_string() + ); + assert_eq!( + bech32m_p2tr, + BitcoinAddress::from_string(bech32m_p2tr) + .unwrap() + .to_string() + ); + } + + #[test] + fn test_from_string_regtest() { + // Legacy addresses have same format as testnet. + // However these are created from bitcoind in regtest mode, + // so including them for completeness + let legacy_p2pkh = utils::REGTEST_ADDR_LEGACY_P2PKH; + let legacy_p2sh = utils::REGTEST_ADDR_LEGACY_P2SH; + let bech32_p2wpkh = utils::REGTEST_ADDR_BECH32_P2WPKH; + let bech32_p2wsh = utils::REGTEST_ADDR_BECH32_P2WSH; + let bech32m_p2tr = utils::REGTEST_ADDR_BECH32M_P2TR; + + assert_eq!( + legacy_p2pkh, + BitcoinAddress::from_string(legacy_p2pkh) + .unwrap() + .to_string() + ); + assert_eq!( + legacy_p2sh, + BitcoinAddress::from_string(legacy_p2sh) + .unwrap() + .to_string() + ); + assert_eq!( + bech32_p2wpkh, + BitcoinAddress::from_string(bech32_p2wpkh) + .unwrap() + .to_string() + ); + assert_eq!( + bech32_p2wsh, + BitcoinAddress::from_string(bech32_p2wsh) + .unwrap() + .to_string() + ); + assert_eq!( + bech32m_p2tr, + BitcoinAddress::from_string(bech32m_p2tr) + .unwrap() + .to_string() + ); + } } diff --git a/stackslib/src/burnchains/bitcoin/bits.rs b/stackslib/src/burnchains/bitcoin/bits.rs index 6a0ae525c9..9bd1de688f 100644 --- a/stackslib/src/burnchains/bitcoin/bits.rs +++ b/stackslib/src/burnchains/bitcoin/bits.rs @@ -1112,7 +1112,7 @@ mod tests { ], vec![ BitcoinTxOutput { - address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(true, [0x17, 0x3f, 0xd3, 0x10, 0xe9, 0xdb, 0x2c, 0x7e, 0x95, 0x50, 0xce, 0x0f, 0x03, 0xf1, 0xe6, 0xc0, 0x1d, 0x83, 0x3a, 0xa9])), + address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(BitcoinNetworkType::Mainnet, [0x17, 0x3f, 0xd3, 0x10, 0xe9, 0xdb, 0x2c, 0x7e, 0x95, 0x50, 0xce, 0x0f, 0x03, 0xf1, 0xe6, 0xc0, 0x1d, 0x83, 0x3a, 0xa9])), units: 965300 } ] @@ -1405,7 +1405,7 @@ mod tests { .into_script(), result: Some(BitcoinTxOutput { address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH( - true, + BitcoinNetworkType::Mainnet, [ 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, @@ -1425,7 +1425,7 @@ mod tests { .into_script(), result: Some(BitcoinTxOutput { address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH( - true, + BitcoinNetworkType::Mainnet, [ 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19, 0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1, @@ -1446,7 +1446,7 @@ mod tests { .into_script(), result: Some(BitcoinTxOutput { address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR( - true, + BitcoinNetworkType::Mainnet, [ 0x33, 0x9c, 0xe7, 0xe1, 0x65, 0xe6, 0x7d, 0x93, 0xad, 0xb3, 0xfe, 0xf8, 0x8a, 0x6d, 0x4b, 0xee, 0xd3, 0x3f, 0x01, 0xfa, 0x87, 0x6f, 0x05, 0xa2, diff --git a/stackslib/src/burnchains/bitcoin/mod.rs b/stackslib/src/burnchains/bitcoin/mod.rs index a8062a233a..5c97d052fa 100644 --- a/stackslib/src/burnchains/bitcoin/mod.rs +++ b/stackslib/src/burnchains/bitcoin/mod.rs @@ -153,6 +153,16 @@ pub enum BitcoinNetworkType { Regtest, } +impl BitcoinNetworkType { + /// Returns `true` if this network type is [`BitcoinNetworkType::Mainnet`]. + pub fn is_mainnet(&self) -> bool { + match *self { + BitcoinNetworkType::Mainnet => true, + _ => false, + } + } +} + #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct BitcoinTxOutput { pub address: BitcoinAddress, diff --git a/stackslib/src/chainstate/burn/operations/test/mod.rs b/stackslib/src/chainstate/burn/operations/test/mod.rs index b737726ae9..331d2bb6c2 100644 --- a/stackslib/src/chainstate/burn/operations/test/mod.rs +++ b/stackslib/src/chainstate/burn/operations/test/mod.rs @@ -9,7 +9,8 @@ use stacks_common::util::hash::Hash160; use crate::burnchains::bitcoin::address::{BitcoinAddress, SegwitBitcoinAddress}; use crate::burnchains::bitcoin::{ - BitcoinInputType, BitcoinTransaction, BitcoinTxInputStructured, BitcoinTxOutput, + BitcoinInputType, BitcoinNetworkType, BitcoinTransaction, BitcoinTxInputStructured, + BitcoinTxOutput, }; use crate::burnchains::{BurnchainBlockHeader, BurnchainSigner, BurnchainTransaction, Txid}; use crate::chainstate::burn::operations::{ @@ -89,7 +90,10 @@ impl Output { pub(crate) fn as_bitcoin_tx_output(&self) -> BitcoinTxOutput { BitcoinTxOutput { units: self.amount, - address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(true, self.address)), + address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR( + BitcoinNetworkType::Mainnet, + self.address, + )), } } } diff --git a/stackslib/src/chainstate/stacks/address.rs b/stackslib/src/chainstate/stacks/address.rs index bec2d0d628..f5dd508ed5 100644 --- a/stackslib/src/chainstate/stacks/address.rs +++ b/stackslib/src/chainstate/stacks/address.rs @@ -26,7 +26,7 @@ use crate::burnchains::bitcoin::address::{ to_c32_version_byte, BitcoinAddress, LegacyBitcoinAddress, LegacyBitcoinAddressType, SegwitBitcoinAddress, }; -use crate::burnchains::bitcoin::BitcoinTxOutput; +use crate::burnchains::bitcoin::{BitcoinNetworkType, BitcoinTxOutput}; use crate::burnchains::Address; use crate::util_lib::boot::boot_code_addr; @@ -375,23 +375,37 @@ impl PoxAddress { } } + /// Fallback to convert `mainnet` bool to a BitcoinNetworkType + /// preserving previous behaviour where a method was expecting the + /// `mainnet' flag + fn network_type_from_mainnet_fallback(mainnet: bool) -> BitcoinNetworkType { + if mainnet { + BitcoinNetworkType::Mainnet + } else { + BitcoinNetworkType::Testnet + } + } + /// Convert this PoxAddress into a base58check string pub fn to_b58(self) -> String { match self { PoxAddress::Standard(addr, _) => addr.to_b58(), PoxAddress::Addr20(mainnet, addrtype, addrbytes) => match addrtype { PoxAddressType20::P2WPKH => { - let btc_addr = SegwitBitcoinAddress::P2WPKH(mainnet, addrbytes); + let network = PoxAddress::network_type_from_mainnet_fallback(mainnet); + let btc_addr = SegwitBitcoinAddress::P2WPKH(network, addrbytes); btc_addr.to_bech32() } }, PoxAddress::Addr32(mainnet, addrtype, addrbytes) => match addrtype { PoxAddressType32::P2WSH => { - let btc_addr = SegwitBitcoinAddress::P2WSH(mainnet, addrbytes); + let network = PoxAddress::network_type_from_mainnet_fallback(mainnet); + let btc_addr = SegwitBitcoinAddress::P2WSH(network, addrbytes); btc_addr.to_bech32() } PoxAddressType32::P2TR => { - let btc_addr = SegwitBitcoinAddress::P2TR(mainnet, addrbytes); + let network = PoxAddress::network_type_from_mainnet_fallback(mainnet); + let btc_addr = SegwitBitcoinAddress::P2TR(network, addrbytes); btc_addr.to_bech32() } }, @@ -447,14 +461,14 @@ impl PoxAddress { let pox_addr = PoxAddress::Standard(addr, None); Some(pox_addr) } - BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(is_mainnet, bytes_20)) => Some( - PoxAddress::Addr20(*is_mainnet, PoxAddressType20::P2WPKH, *bytes_20), + BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(network, bytes_20)) => Some( + PoxAddress::Addr20(network.is_mainnet(), PoxAddressType20::P2WPKH, *bytes_20), ), - BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH(is_mainnet, bytes_32)) => Some( - PoxAddress::Addr32(*is_mainnet, PoxAddressType32::P2WSH, *bytes_32), + BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH(network, bytes_32)) => Some( + PoxAddress::Addr32(network.is_mainnet(), PoxAddressType32::P2WSH, *bytes_32), ), - BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(is_mainnet, bytes_32)) => Some( - PoxAddress::Addr32(*is_mainnet, PoxAddressType32::P2TR, *bytes_32), + BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(network, bytes_32)) => Some( + PoxAddress::Addr32(network.is_mainnet(), PoxAddressType32::P2TR, *bytes_32), ), } }