Skip to content

Commit fc25cd7

Browse files
committed
feat: add is_valid_for_network to address
1 parent cdec63e commit fc25cd7

File tree

6 files changed

+389
-1
lines changed

6 files changed

+389
-1
lines changed

bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/OfflineWalletTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ class OfflineWalletTest {
3030
)
3131
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
3232

33+
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
34+
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")
35+
assertFalse(addressInfo.address.isValidForNetwork(Network.REGTEST), "Address is valid for regtest network, but it shouldn't be")
36+
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
37+
3338
assertEquals(
3439
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
3540
actual = addressInfo.address.asString()

bdk-ffi/src/bdk.udl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ interface Address {
315315
string to_qr_uri();
316316

317317
string as_string();
318+
319+
boolean is_valid_for_network(Network network);
318320
};
319321

320322
interface Transaction {

bdk-ffi/src/bitcoin.rs

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ impl From<BdkScriptBuf> for Script {
3434
}
3535
}
3636

37+
#[derive(PartialEq, Debug)]
3738
pub enum Network {
3839
Bitcoin,
3940
Testnet,
@@ -121,6 +122,15 @@ impl Address {
121122
pub fn as_string(&self) -> String {
122123
self.inner.to_string()
123124
}
125+
126+
pub fn is_valid_for_network(&self, network: Network) -> bool {
127+
let address_str = self.inner.to_string();
128+
if let Ok(unchecked_address) = address_str.parse::<BdkAddress<NetworkUnchecked>>() {
129+
unchecked_address.is_valid_for_network(network.into())
130+
} else {
131+
false
132+
}
133+
}
124134
}
125135

126136
impl From<Address> for BdkAddress {
@@ -314,3 +324,354 @@ impl From<&BdkTxOut> for TxOut {
314324
}
315325
}
316326
}
327+
328+
#[cfg(test)]
329+
mod tests {
330+
use crate::bitcoin::Address;
331+
use crate::bitcoin::Network;
332+
333+
#[test]
334+
fn test_is_valid_for_network() {
335+
// ====Docs tests====
336+
// https://docs.rs/bitcoin/0.29.2/src/bitcoin/util/address.rs.html#798-802
337+
338+
let docs_address_testnet_str = "2N83imGV3gPwBzKJQvWJ7cRUY2SpUyU6A5e";
339+
let docs_address_testnet =
340+
Address::new(docs_address_testnet_str.to_string(), Network::Testnet).unwrap();
341+
assert!(
342+
docs_address_testnet.is_valid_for_network(Network::Testnet),
343+
"Address should be valid for Testnet"
344+
);
345+
assert!(
346+
docs_address_testnet.is_valid_for_network(Network::Signet),
347+
"Address should be valid for Signet"
348+
);
349+
assert!(
350+
docs_address_testnet.is_valid_for_network(Network::Regtest),
351+
"Address should be valid for Regtest"
352+
);
353+
assert_ne!(
354+
docs_address_testnet.network(),
355+
Network::Bitcoin,
356+
"Address should not be parsed as Bitcoin"
357+
);
358+
359+
let docs_address_mainnet_str = "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf";
360+
let docs_address_mainnet =
361+
Address::new(docs_address_mainnet_str.to_string(), Network::Bitcoin).unwrap();
362+
assert!(
363+
docs_address_mainnet.is_valid_for_network(Network::Bitcoin),
364+
"Address should be valid for Bitcoin"
365+
);
366+
assert_ne!(
367+
docs_address_mainnet.network(),
368+
Network::Testnet,
369+
"Address should not be valid for Testnet"
370+
);
371+
assert_ne!(
372+
docs_address_mainnet.network(),
373+
Network::Signet,
374+
"Address should not be valid for Signet"
375+
);
376+
assert_ne!(
377+
docs_address_mainnet.network(),
378+
Network::Regtest,
379+
"Address should not be valid for Regtest"
380+
);
381+
382+
// ====Bech32====
383+
384+
// | Network | Prefix | Address Type |
385+
// |-----------------|---------|--------------|
386+
// | Bitcoin Mainnet | `bc1` | Bech32 |
387+
// | Bitcoin Testnet | `tb1` | Bech32 |
388+
// | Bitcoin Signet | `tb1` | Bech32 |
389+
// | Bitcoin Regtest | `bcrt1` | Bech32 |
390+
391+
// Bech32 - Bitcoin
392+
// Valid for:
393+
// - Bitcoin
394+
// Not valid for:
395+
// - Testnet
396+
// - Signet
397+
// - Regtest
398+
let bitcoin_mainnet_bech32_address_str = "bc1qxhmdufsvnuaaaer4ynz88fspdsxq2h9e9cetdj";
399+
let bitcoin_mainnet_bech32_address = Address::new(
400+
bitcoin_mainnet_bech32_address_str.to_string(),
401+
Network::Bitcoin,
402+
)
403+
.unwrap();
404+
assert!(
405+
bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Bitcoin),
406+
"Address should be valid for Bitcoin"
407+
);
408+
assert!(
409+
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Testnet),
410+
"Address should not be valid for Testnet"
411+
);
412+
assert!(
413+
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Signet),
414+
"Address should not be valid for Signet"
415+
);
416+
assert!(
417+
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Regtest),
418+
"Address should not be valid for Regtest"
419+
);
420+
421+
// Bech32 - Testnet
422+
// Valid for:
423+
// - Testnet
424+
// - Regtest
425+
// Not valid for:
426+
// - Bitcoin
427+
// - Regtest
428+
let bitcoin_testnet_bech32_address_str =
429+
"tb1p4nel7wkc34raczk8c4jwk5cf9d47u2284rxn98rsjrs4w3p2sheqvjmfdh";
430+
let bitcoin_testnet_bech32_address = Address::new(
431+
bitcoin_testnet_bech32_address_str.to_string(),
432+
Network::Testnet,
433+
)
434+
.unwrap();
435+
assert!(
436+
!bitcoin_testnet_bech32_address.is_valid_for_network(Network::Bitcoin),
437+
"Address should not be valid for Bitcoin"
438+
);
439+
assert!(
440+
bitcoin_testnet_bech32_address.is_valid_for_network(Network::Testnet),
441+
"Address should be valid for Testnet"
442+
);
443+
assert!(
444+
bitcoin_testnet_bech32_address.is_valid_for_network(Network::Signet),
445+
"Address should be valid for Signet"
446+
);
447+
assert!(
448+
!bitcoin_testnet_bech32_address.is_valid_for_network(Network::Regtest),
449+
"Address should not not be valid for Regtest"
450+
);
451+
452+
// Bech32 - Signet
453+
// Valid for:
454+
// - Signet
455+
// - Testnet
456+
// Not valid for:
457+
// - Bitcoin
458+
// - Regtest
459+
let bitcoin_signet_bech32_address_str =
460+
"tb1pwzv7fv35yl7ypwj8w7al2t8apd6yf4568cs772qjwper74xqc99sk8x7tk";
461+
let bitcoin_signet_bech32_address = Address::new(
462+
bitcoin_signet_bech32_address_str.to_string(),
463+
Network::Signet,
464+
)
465+
.unwrap();
466+
assert!(
467+
!bitcoin_signet_bech32_address.is_valid_for_network(Network::Bitcoin),
468+
"Address should not be valid for Bitcoin"
469+
);
470+
assert!(
471+
bitcoin_signet_bech32_address.is_valid_for_network(Network::Testnet),
472+
"Address should be valid for Testnet"
473+
);
474+
assert!(
475+
bitcoin_signet_bech32_address.is_valid_for_network(Network::Signet),
476+
"Address should be valid for Signet"
477+
);
478+
assert!(
479+
!bitcoin_signet_bech32_address.is_valid_for_network(Network::Regtest),
480+
"Address should not not be valid for Regtest"
481+
);
482+
483+
// Bech32 - Regtest
484+
// Valid for:
485+
// - Regtest
486+
// Not valid for:
487+
// - Bitcoin
488+
// - Testnet
489+
// - Signet
490+
let bitcoin_regtest_bech32_address_str = "bcrt1q39c0vrwpgfjkhasu5mfke9wnym45nydfwaeems";
491+
let bitcoin_regtest_bech32_address = Address::new(
492+
bitcoin_regtest_bech32_address_str.to_string(),
493+
Network::Regtest,
494+
)
495+
.unwrap();
496+
assert!(
497+
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Bitcoin),
498+
"Address should not be valid for Bitcoin"
499+
);
500+
assert!(
501+
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Testnet),
502+
"Address should not be valid for Testnet"
503+
);
504+
assert!(
505+
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Signet),
506+
"Address should not be valid for Signet"
507+
);
508+
assert!(
509+
bitcoin_regtest_bech32_address.is_valid_for_network(Network::Regtest),
510+
"Address should be valid for Regtest"
511+
);
512+
513+
// ====P2PKH====
514+
515+
// | Network | Prefix for P2PKH | Prefix for P2SH |
516+
// |------------------------------------|------------------|-----------------|
517+
// | Bitcoin Mainnet | `1` | `3` |
518+
// | Bitcoin Testnet, Regtest, Signet | `m` or `n` | `2` |
519+
520+
// P2PKH - Bitcoin
521+
// Valid for:
522+
// - Bitcoin
523+
// Not valid for:
524+
// - Testnet
525+
// - Regtest
526+
let bitcoin_mainnet_p2pkh_address_str = "1FfmbHfnpaZjKFvyi1okTjJJusN455paPH";
527+
let bitcoin_mainnet_p2pkh_address = Address::new(
528+
bitcoin_mainnet_p2pkh_address_str.to_string(),
529+
Network::Bitcoin,
530+
)
531+
.unwrap();
532+
assert!(
533+
bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Bitcoin),
534+
"Address should be valid for Bitcoin"
535+
);
536+
assert!(
537+
!bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Testnet),
538+
"Address should not be valid for Testnet"
539+
);
540+
assert!(
541+
!bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Regtest),
542+
"Address should not be valid for Regtest"
543+
);
544+
545+
// P2PKH - Testnet
546+
// Valid for:
547+
// - Testnet
548+
// - Regtest
549+
// Not valid for:
550+
// - Bitcoin
551+
let bitcoin_testnet_p2pkh_address_str = "mucFNhKMYoBQYUAEsrFVscQ1YaFQPekBpg";
552+
let bitcoin_testnet_p2pkh_address = Address::new(
553+
bitcoin_testnet_p2pkh_address_str.to_string(),
554+
Network::Testnet,
555+
)
556+
.unwrap();
557+
assert!(
558+
!bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Bitcoin),
559+
"Address should not be valid for Bitcoin"
560+
);
561+
assert!(
562+
bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Testnet),
563+
"Address should be valid for Testnet"
564+
);
565+
assert!(
566+
bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Regtest),
567+
"Address should be valid for Regtest"
568+
);
569+
570+
// P2PKH - Regtest
571+
// Valid for:
572+
// - Testnet
573+
// - Regtest
574+
// Not valid for:
575+
// - Bitcoin
576+
let bitcoin_regtest_p2pkh_address_str = "msiGFK1PjCk8E6FXeoGkQPTscmcpyBdkgS";
577+
let bitcoin_regtest_p2pkh_address = Address::new(
578+
bitcoin_regtest_p2pkh_address_str.to_string(),
579+
Network::Regtest,
580+
)
581+
.unwrap();
582+
assert!(
583+
!bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Bitcoin),
584+
"Address should not be valid for Bitcoin"
585+
);
586+
assert!(
587+
bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Testnet),
588+
"Address should be valid for Testnet"
589+
);
590+
assert!(
591+
bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Regtest),
592+
"Address should be valid for Regtest"
593+
);
594+
595+
// ====P2SH====
596+
597+
// | Network | Prefix for P2PKH | Prefix for P2SH |
598+
// |------------------------------------|------------------|-----------------|
599+
// | Bitcoin Mainnet | `1` | `3` |
600+
// | Bitcoin Testnet, Regtest, Signet | `m` or `n` | `2` |
601+
602+
// P2SH - Bitcoin
603+
// Valid for:
604+
// - Bitcoin
605+
// Not valid for:
606+
// - Testnet
607+
// - Regtest
608+
let bitcoin_mainnet_p2sh_address_str = "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy";
609+
let bitcoin_mainnet_p2sh_address = Address::new(
610+
bitcoin_mainnet_p2sh_address_str.to_string(),
611+
Network::Bitcoin,
612+
)
613+
.unwrap();
614+
assert!(
615+
bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Bitcoin),
616+
"Address should be valid for Bitcoin"
617+
);
618+
assert!(
619+
!bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Testnet),
620+
"Address should not be valid for Testnet"
621+
);
622+
assert!(
623+
!bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Regtest),
624+
"Address should not be valid for Regtest"
625+
);
626+
627+
// P2SH - Testnet
628+
// Valid for:
629+
// - Testnet
630+
// - Regtest
631+
// Not valid for:
632+
// - Bitcoin
633+
let bitcoin_testnet_p2sh_address_str = "2NFUBBRcTJbYc1D4HSCbJhKZp6YCV4PQFpQ";
634+
let bitcoin_testnet_p2sh_address = Address::new(
635+
bitcoin_testnet_p2sh_address_str.to_string(),
636+
Network::Testnet,
637+
)
638+
.unwrap();
639+
assert!(
640+
!bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Bitcoin),
641+
"Address should not be valid for Bitcoin"
642+
);
643+
assert!(
644+
bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Testnet),
645+
"Address should be valid for Testnet"
646+
);
647+
assert!(
648+
bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Regtest),
649+
"Address should be valid for Regtest"
650+
);
651+
652+
// P2SH - Regtest
653+
// Valid for:
654+
// - Testnet
655+
// - Regtest
656+
// Not valid for:
657+
// - Bitcoin
658+
let bitcoin_regtest_p2sh_address_str = "2NEb8N5B9jhPUCBchz16BB7bkJk8VCZQjf3";
659+
let bitcoin_regtest_p2sh_address = Address::new(
660+
bitcoin_regtest_p2sh_address_str.to_string(),
661+
Network::Regtest,
662+
)
663+
.unwrap();
664+
assert!(
665+
!bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Bitcoin),
666+
"Address should not be valid for Bitcoin"
667+
);
668+
assert!(
669+
bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Testnet),
670+
"Address should be valid for Testnet"
671+
);
672+
assert!(
673+
bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Regtest),
674+
"Address should be valid for Regtest"
675+
);
676+
}
677+
}

0 commit comments

Comments
 (0)