Skip to content

Commit 457ec9e

Browse files
committed
fix: add regtest hrp for segwit bitcoin address, #6366
1 parent a9e2828 commit 457ec9e

File tree

5 files changed

+95
-64
lines changed

5 files changed

+95
-64
lines changed

stackslib/src/burnchains/bitcoin/address.rs

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ pub struct LegacyBitcoinAddress {
4545
pub bytes: Hash160,
4646
}
4747

48-
/// Segwit address. The bool member is "mainnet" (to determine the hrp)
48+
/// Segwit address. The [`BitcoinNetworkType`] member allows to to determine the HRP
4949
/// New in 2.1
5050
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
5151
pub enum SegwitBitcoinAddress {
52-
P2WPKH(bool, [u8; 20]),
53-
P2WSH(bool, [u8; 32]),
54-
P2TR(bool, [u8; 32]),
52+
P2WPKH(BitcoinNetworkType, [u8; 20]),
53+
P2WSH(BitcoinNetworkType, [u8; 32]),
54+
P2TR(BitcoinNetworkType, [u8; 32]),
5555
}
5656

5757
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
@@ -81,6 +81,7 @@ pub const ADDRESS_VERSION_TESTNET_MULTISIG: u8 = 196;
8181
// segwit hrps
8282
pub const SEGWIT_MAINNET_HRP: &str = "bc";
8383
pub const SEGWIT_TESTNET_HRP: &str = "tb";
84+
pub const SEGWIT_REGTEST_HRP: &str = "bcrt";
8485

8586
// segwit witnes versions
8687
pub const SEGWIT_V0: u8 = 0;
@@ -155,15 +156,6 @@ pub fn to_b58_version_byte(version: u8) -> Option<u8> {
155156
}
156157
}
157158

158-
/// Get the HRP for a segwit address based on whether or not we're in mainnet
159-
pub fn segwit_hrp(mainnet: bool) -> &'static str {
160-
if mainnet {
161-
SEGWIT_MAINNET_HRP
162-
} else {
163-
SEGWIT_TESTNET_HRP
164-
}
165-
}
166-
167159
impl LegacyBitcoinAddress {
168160
fn to_versioned_bytes(&self) -> [u8; 21] {
169161
let mut ret = [0; 21];
@@ -269,11 +261,26 @@ impl SegwitBitcoinAddress {
269261
version_bytes
270262
}
271263

264+
/// Returns `true` if this Segwit address belongs to the Mainnet network.
272265
pub fn is_mainnet(&self) -> bool {
266+
self.network().is_mainnet()
267+
}
268+
269+
/// Returns the Bitcoin network type associated with this Segwit address.
270+
pub fn network(&self) -> BitcoinNetworkType {
273271
match *self {
274-
SegwitBitcoinAddress::P2WPKH(ref mainnet, _) => *mainnet,
275-
SegwitBitcoinAddress::P2WSH(ref mainnet, _) => *mainnet,
276-
SegwitBitcoinAddress::P2TR(ref mainnet, _) => *mainnet,
272+
SegwitBitcoinAddress::P2WPKH(ref network, _)
273+
| SegwitBitcoinAddress::P2WSH(ref network, _)
274+
| SegwitBitcoinAddress::P2TR(ref network, _) => *network,
275+
}
276+
}
277+
278+
/// Returns the HRP string associated with address network
279+
pub fn hrp(&self) -> &'static str {
280+
match self.network() {
281+
BitcoinNetworkType::Mainnet => SEGWIT_MAINNET_HRP,
282+
BitcoinNetworkType::Testnet => SEGWIT_TESTNET_HRP,
283+
BitcoinNetworkType::Regtest => SEGWIT_REGTEST_HRP,
277284
}
278285
}
279286

@@ -295,28 +302,28 @@ impl SegwitBitcoinAddress {
295302
}
296303

297304
pub fn to_bech32(&self) -> String {
298-
let hrp = segwit_hrp(self.is_mainnet());
299-
self.to_bech32_hrp(hrp)
305+
self.to_bech32_hrp(self.hrp())
300306
}
301307

302308
pub fn from_bech32(s: &str) -> Option<SegwitBitcoinAddress> {
303309
let (hrp, quintets, variant) = bech32::decode(s)
304310
.inspect_err(|_e| {
305-
test_debug!("Failed to decode '{s}': {_e:?}");
311+
println!("Failed to decode '{s}': {_e:?}");
306312
})
307313
.ok()?;
308314

309-
let mainnet = if hrp == SEGWIT_MAINNET_HRP {
310-
Some(true)
311-
} else if hrp == SEGWIT_TESTNET_HRP {
312-
Some(false)
313-
} else {
314-
test_debug!("Unrecognized hrp '{:?}'", &hrp);
315-
None
316-
}?;
315+
let network_type = match hrp.as_str() {
316+
SEGWIT_MAINNET_HRP => BitcoinNetworkType::Mainnet,
317+
SEGWIT_TESTNET_HRP => BitcoinNetworkType::Testnet,
318+
SEGWIT_REGTEST_HRP => BitcoinNetworkType::Regtest,
319+
_ => {
320+
println!("Unrecognized hrp '{:?}'", &hrp);
321+
return None;
322+
}
323+
};
317324

318325
if quintets.is_empty() || quintets.len() > 65 {
319-
test_debug!("Invalid prog length: {}", quintets.len());
326+
println!("Invalid prog length: {}", quintets.len());
320327
return None;
321328
}
322329

@@ -326,25 +333,25 @@ impl SegwitBitcoinAddress {
326333

327334
let bytes = Vec::from_base32(&prog)
328335
.inspect_err(|_e| {
329-
test_debug!("Failed to decode quintets: {_e:?}");
336+
println!("Failed to decode quintets: {_e:?}");
330337
})
331338
.ok()?;
332339

333340
match (variant, version, bytes.len()) {
334341
(bech32::Variant::Bech32, SEGWIT_V0, 20) => {
335342
let bytes_20 = bytes.try_into().ok()?;
336-
Some(SegwitBitcoinAddress::P2WPKH(mainnet, bytes_20))
343+
Some(SegwitBitcoinAddress::P2WPKH(network_type, bytes_20))
337344
}
338345
(bech32::Variant::Bech32, SEGWIT_V0, 32) => {
339346
let bytes_32 = bytes.try_into().ok()?;
340-
Some(SegwitBitcoinAddress::P2WSH(mainnet, bytes_32))
347+
Some(SegwitBitcoinAddress::P2WSH(network_type, bytes_32))
341348
}
342349
(bech32::Variant::Bech32m, SEGWIT_V1, 32) => {
343350
let bytes_32 = bytes.try_into().ok()?;
344-
Some(SegwitBitcoinAddress::P2TR(mainnet, bytes_32))
351+
Some(SegwitBitcoinAddress::P2TR(network_type, bytes_32))
345352
}
346353
(_, _, _) => {
347-
test_debug!(
354+
println!(
348355
"Unrecognized segwit address {}: ({:?}, {}, [u8; {}])",
349356
s,
350357
&variant,
@@ -437,9 +444,8 @@ impl BitcoinAddress {
437444
.try_into()
438445
.map_err(|_| btc_error::InvalidByteSequence)?;
439446

440-
let mainnet = network_id == BitcoinNetworkType::Mainnet;
441447
Ok(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(
442-
mainnet, *my_bytes,
448+
network_id, *my_bytes,
443449
)))
444450
}
445451

@@ -480,9 +486,8 @@ impl BitcoinAddress {
480486
// segwit p2wpkh
481487
let witness_program: &[u8; 20] = scriptpubkey.get(2..22)?.try_into().ok()?;
482488

483-
let mainnet = network_id == BitcoinNetworkType::Mainnet;
484489
Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(
485-
mainnet,
490+
network_id,
486491
*witness_program,
487492
)))
488493
} else if scriptpubkey.len() == 34
@@ -491,9 +496,8 @@ impl BitcoinAddress {
491496
// segwit p2wsh
492497
let witness_program: &[u8; 32] = scriptpubkey.get(2..34)?.try_into().ok()?;
493498

494-
let mainnet = network_id == BitcoinNetworkType::Mainnet;
495499
Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH(
496-
mainnet,
500+
network_id,
497501
*witness_program,
498502
)))
499503
} else if scriptpubkey.len() == 34
@@ -502,9 +506,8 @@ impl BitcoinAddress {
502506
// segwit p2tr
503507
let witness_program: &[u8; 32] = scriptpubkey.get(2..34)?.try_into().ok()?;
504508

505-
let mainnet = network_id == BitcoinNetworkType::Mainnet;
506509
Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(
507-
mainnet,
510+
network_id,
508511
*witness_program,
509512
)))
510513
} else {
@@ -771,7 +774,7 @@ mod tests {
771774
AddressFixture {
772775
addr: "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4".to_owned(),
773776
result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(
774-
true,
777+
BitcoinNetworkType::Mainnet,
775778
[
776779
0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
777780
0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
@@ -781,7 +784,7 @@ mod tests {
781784
AddressFixture {
782785
addr: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7".to_owned(),
783786
result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH(
784-
false,
787+
BitcoinNetworkType::Testnet,
785788
[
786789
0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19, 0x20,
787790
0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1,
@@ -808,7 +811,7 @@ mod tests {
808811
AddressFixture {
809812
addr: "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy".to_owned(),
810813
result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH(
811-
false,
814+
BitcoinNetworkType::Testnet,
812815
[
813816
0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1, 0x87,
814817
0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2,
@@ -942,7 +945,7 @@ mod tests {
942945
.unwrap()
943946
.to_vec(),
944947
result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(
945-
true,
948+
BitcoinNetworkType::Mainnet,
946949
[
947950
0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
948951
0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
@@ -957,7 +960,7 @@ mod tests {
957960
.unwrap()
958961
.to_vec(),
959962
result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH(
960-
true,
963+
BitcoinNetworkType::Mainnet,
961964
[
962965
0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19, 0x20,
963966
0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1,
@@ -973,7 +976,7 @@ mod tests {
973976
.unwrap()
974977
.to_vec(),
975978
result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH(
976-
true,
979+
BitcoinNetworkType::Mainnet,
977980
[
978981
0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1, 0x87,
979982
0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2,
@@ -990,7 +993,7 @@ mod tests {
990993
.unwrap()
991994
.to_vec(),
992995
result: Some(BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(
993-
true,
996+
BitcoinNetworkType::Mainnet,
994997
[
995998
0x33, 0x9c, 0xe7, 0xe1, 0x65, 0xe6, 0x7d, 0x93, 0xad, 0xb3, 0xfe, 0xf8,
996999
0x8a, 0x6d, 0x4b, 0xee, 0xd3, 0x3f, 0x01, 0xfa, 0x87, 0x6f, 0x05, 0xa2,

stackslib/src/burnchains/bitcoin/bits.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ mod tests {
11121112
],
11131113
vec![
11141114
BitcoinTxOutput {
1115-
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])),
1115+
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])),
11161116
units: 965300
11171117
}
11181118
]
@@ -1405,7 +1405,7 @@ mod tests {
14051405
.into_script(),
14061406
result: Some(BitcoinTxOutput {
14071407
address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(
1408-
true,
1408+
BitcoinNetworkType::Mainnet,
14091409
[
14101410
0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
14111411
0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
@@ -1425,7 +1425,7 @@ mod tests {
14251425
.into_script(),
14261426
result: Some(BitcoinTxOutput {
14271427
address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH(
1428-
true,
1428+
BitcoinNetworkType::Mainnet,
14291429
[
14301430
0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19, 0x20,
14311431
0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1,
@@ -1446,7 +1446,7 @@ mod tests {
14461446
.into_script(),
14471447
result: Some(BitcoinTxOutput {
14481448
address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(
1449-
true,
1449+
BitcoinNetworkType::Mainnet,
14501450
[
14511451
0x33, 0x9c, 0xe7, 0xe1, 0x65, 0xe6, 0x7d, 0x93, 0xad, 0xb3, 0xfe, 0xf8,
14521452
0x8a, 0x6d, 0x4b, 0xee, 0xd3, 0x3f, 0x01, 0xfa, 0x87, 0x6f, 0x05, 0xa2,

stackslib/src/burnchains/bitcoin/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,16 @@ pub enum BitcoinNetworkType {
153153
Regtest,
154154
}
155155

156+
impl BitcoinNetworkType {
157+
/// Returns `true` if this network type is [`BitcoinNetworkType::Mainnet`].
158+
pub fn is_mainnet(&self) -> bool {
159+
match *self {
160+
BitcoinNetworkType::Mainnet => true,
161+
_ => false,
162+
}
163+
}
164+
}
165+
156166
#[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)]
157167
pub struct BitcoinTxOutput {
158168
pub address: BitcoinAddress,

stackslib/src/chainstate/burn/operations/test/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use stacks_common::util::hash::Hash160;
99

1010
use crate::burnchains::bitcoin::address::{BitcoinAddress, SegwitBitcoinAddress};
1111
use crate::burnchains::bitcoin::{
12-
BitcoinInputType, BitcoinTransaction, BitcoinTxInputStructured, BitcoinTxOutput,
12+
BitcoinInputType, BitcoinNetworkType, BitcoinTransaction, BitcoinTxInputStructured,
13+
BitcoinTxOutput,
1314
};
1415
use crate::burnchains::{BurnchainBlockHeader, BurnchainSigner, BurnchainTransaction, Txid};
1516
use crate::chainstate::burn::operations::{
@@ -89,7 +90,10 @@ impl Output {
8990
pub(crate) fn as_bitcoin_tx_output(&self) -> BitcoinTxOutput {
9091
BitcoinTxOutput {
9192
units: self.amount,
92-
address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(true, self.address)),
93+
address: BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(
94+
BitcoinNetworkType::Mainnet,
95+
self.address,
96+
)),
9397
}
9498
}
9599
}

stackslib/src/chainstate/stacks/address.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::burnchains::bitcoin::address::{
2626
to_c32_version_byte, BitcoinAddress, LegacyBitcoinAddress, LegacyBitcoinAddressType,
2727
SegwitBitcoinAddress,
2828
};
29-
use crate::burnchains::bitcoin::BitcoinTxOutput;
29+
use crate::burnchains::bitcoin::{BitcoinNetworkType, BitcoinTxOutput};
3030
use crate::burnchains::Address;
3131
use crate::util_lib::boot::boot_code_addr;
3232

@@ -375,23 +375,37 @@ impl PoxAddress {
375375
}
376376
}
377377

378+
/// Fallback to convert `mainnet` bool to a BitcoinNetworkType
379+
/// preserving previous behaviour where a method was expecting the
380+
/// `mainnet' flag
381+
fn network_type_from_mainnet_fallback(mainnet: bool) -> BitcoinNetworkType {
382+
if mainnet {
383+
BitcoinNetworkType::Mainnet
384+
} else {
385+
BitcoinNetworkType::Testnet
386+
}
387+
}
388+
378389
/// Convert this PoxAddress into a base58check string
379390
pub fn to_b58(self) -> String {
380391
match self {
381392
PoxAddress::Standard(addr, _) => addr.to_b58(),
382393
PoxAddress::Addr20(mainnet, addrtype, addrbytes) => match addrtype {
383394
PoxAddressType20::P2WPKH => {
384-
let btc_addr = SegwitBitcoinAddress::P2WPKH(mainnet, addrbytes);
395+
let network = PoxAddress::network_type_from_mainnet_fallback(mainnet);
396+
let btc_addr = SegwitBitcoinAddress::P2WPKH(network, addrbytes);
385397
btc_addr.to_bech32()
386398
}
387399
},
388400
PoxAddress::Addr32(mainnet, addrtype, addrbytes) => match addrtype {
389401
PoxAddressType32::P2WSH => {
390-
let btc_addr = SegwitBitcoinAddress::P2WSH(mainnet, addrbytes);
402+
let network = PoxAddress::network_type_from_mainnet_fallback(mainnet);
403+
let btc_addr = SegwitBitcoinAddress::P2WSH(network, addrbytes);
391404
btc_addr.to_bech32()
392405
}
393406
PoxAddressType32::P2TR => {
394-
let btc_addr = SegwitBitcoinAddress::P2TR(mainnet, addrbytes);
407+
let network = PoxAddress::network_type_from_mainnet_fallback(mainnet);
408+
let btc_addr = SegwitBitcoinAddress::P2TR(network, addrbytes);
395409
btc_addr.to_bech32()
396410
}
397411
},
@@ -447,14 +461,14 @@ impl PoxAddress {
447461
let pox_addr = PoxAddress::Standard(addr, None);
448462
Some(pox_addr)
449463
}
450-
BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(is_mainnet, bytes_20)) => Some(
451-
PoxAddress::Addr20(*is_mainnet, PoxAddressType20::P2WPKH, *bytes_20),
464+
BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WPKH(network, bytes_20)) => Some(
465+
PoxAddress::Addr20(network.is_mainnet(), PoxAddressType20::P2WPKH, *bytes_20),
452466
),
453-
BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH(is_mainnet, bytes_32)) => Some(
454-
PoxAddress::Addr32(*is_mainnet, PoxAddressType32::P2WSH, *bytes_32),
467+
BitcoinAddress::Segwit(SegwitBitcoinAddress::P2WSH(network, bytes_32)) => Some(
468+
PoxAddress::Addr32(network.is_mainnet(), PoxAddressType32::P2WSH, *bytes_32),
455469
),
456-
BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(is_mainnet, bytes_32)) => Some(
457-
PoxAddress::Addr32(*is_mainnet, PoxAddressType32::P2TR, *bytes_32),
470+
BitcoinAddress::Segwit(SegwitBitcoinAddress::P2TR(network, bytes_32)) => Some(
471+
PoxAddress::Addr32(network.is_mainnet(), PoxAddressType32::P2TR, *bytes_32),
458472
),
459473
}
460474
}

0 commit comments

Comments
 (0)