feat: add is_valid_for_network to address

This commit is contained in:
Matthew 2024-01-03 16:57:57 -06:00
parent cdec63efa3
commit fc25cd709a
No known key found for this signature in database
GPG Key ID: 8D4FCD82DD54DDD2
6 changed files with 389 additions and 1 deletions

View File

@ -30,6 +30,11 @@ class OfflineWalletTest {
)
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")
assertFalse(addressInfo.address.isValidForNetwork(Network.REGTEST), "Address is valid for regtest network, but it shouldn't be")
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
assertEquals(
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
actual = addressInfo.address.asString()

View File

@ -315,6 +315,8 @@ interface Address {
string to_qr_uri();
string as_string();
boolean is_valid_for_network(Network network);
};
interface Transaction {

View File

@ -34,6 +34,7 @@ impl From<BdkScriptBuf> for Script {
}
}
#[derive(PartialEq, Debug)]
pub enum Network {
Bitcoin,
Testnet,
@ -121,6 +122,15 @@ impl Address {
pub fn as_string(&self) -> String {
self.inner.to_string()
}
pub fn is_valid_for_network(&self, network: Network) -> bool {
let address_str = self.inner.to_string();
if let Ok(unchecked_address) = address_str.parse::<BdkAddress<NetworkUnchecked>>() {
unchecked_address.is_valid_for_network(network.into())
} else {
false
}
}
}
impl From<Address> for BdkAddress {
@ -314,3 +324,354 @@ impl From<&BdkTxOut> for TxOut {
}
}
}
#[cfg(test)]
mod tests {
use crate::bitcoin::Address;
use crate::bitcoin::Network;
#[test]
fn test_is_valid_for_network() {
// ====Docs tests====
// https://docs.rs/bitcoin/0.29.2/src/bitcoin/util/address.rs.html#798-802
let docs_address_testnet_str = "2N83imGV3gPwBzKJQvWJ7cRUY2SpUyU6A5e";
let docs_address_testnet =
Address::new(docs_address_testnet_str.to_string(), Network::Testnet).unwrap();
assert!(
docs_address_testnet.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
docs_address_testnet.is_valid_for_network(Network::Signet),
"Address should be valid for Signet"
);
assert!(
docs_address_testnet.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
assert_ne!(
docs_address_testnet.network(),
Network::Bitcoin,
"Address should not be parsed as Bitcoin"
);
let docs_address_mainnet_str = "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf";
let docs_address_mainnet =
Address::new(docs_address_mainnet_str.to_string(), Network::Bitcoin).unwrap();
assert!(
docs_address_mainnet.is_valid_for_network(Network::Bitcoin),
"Address should be valid for Bitcoin"
);
assert_ne!(
docs_address_mainnet.network(),
Network::Testnet,
"Address should not be valid for Testnet"
);
assert_ne!(
docs_address_mainnet.network(),
Network::Signet,
"Address should not be valid for Signet"
);
assert_ne!(
docs_address_mainnet.network(),
Network::Regtest,
"Address should not be valid for Regtest"
);
// ====Bech32====
// | Network | Prefix | Address Type |
// |-----------------|---------|--------------|
// | Bitcoin Mainnet | `bc1` | Bech32 |
// | Bitcoin Testnet | `tb1` | Bech32 |
// | Bitcoin Signet | `tb1` | Bech32 |
// | Bitcoin Regtest | `bcrt1` | Bech32 |
// Bech32 - Bitcoin
// Valid for:
// - Bitcoin
// Not valid for:
// - Testnet
// - Signet
// - Regtest
let bitcoin_mainnet_bech32_address_str = "bc1qxhmdufsvnuaaaer4ynz88fspdsxq2h9e9cetdj";
let bitcoin_mainnet_bech32_address = Address::new(
bitcoin_mainnet_bech32_address_str.to_string(),
Network::Bitcoin,
)
.unwrap();
assert!(
bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Bitcoin),
"Address should be valid for Bitcoin"
);
assert!(
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Testnet),
"Address should not be valid for Testnet"
);
assert!(
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Signet),
"Address should not be valid for Signet"
);
assert!(
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Regtest),
"Address should not be valid for Regtest"
);
// Bech32 - Testnet
// Valid for:
// - Testnet
// - Regtest
// Not valid for:
// - Bitcoin
// - Regtest
let bitcoin_testnet_bech32_address_str =
"tb1p4nel7wkc34raczk8c4jwk5cf9d47u2284rxn98rsjrs4w3p2sheqvjmfdh";
let bitcoin_testnet_bech32_address = Address::new(
bitcoin_testnet_bech32_address_str.to_string(),
Network::Testnet,
)
.unwrap();
assert!(
!bitcoin_testnet_bech32_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_testnet_bech32_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_testnet_bech32_address.is_valid_for_network(Network::Signet),
"Address should be valid for Signet"
);
assert!(
!bitcoin_testnet_bech32_address.is_valid_for_network(Network::Regtest),
"Address should not not be valid for Regtest"
);
// Bech32 - Signet
// Valid for:
// - Signet
// - Testnet
// Not valid for:
// - Bitcoin
// - Regtest
let bitcoin_signet_bech32_address_str =
"tb1pwzv7fv35yl7ypwj8w7al2t8apd6yf4568cs772qjwper74xqc99sk8x7tk";
let bitcoin_signet_bech32_address = Address::new(
bitcoin_signet_bech32_address_str.to_string(),
Network::Signet,
)
.unwrap();
assert!(
!bitcoin_signet_bech32_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_signet_bech32_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_signet_bech32_address.is_valid_for_network(Network::Signet),
"Address should be valid for Signet"
);
assert!(
!bitcoin_signet_bech32_address.is_valid_for_network(Network::Regtest),
"Address should not not be valid for Regtest"
);
// Bech32 - Regtest
// Valid for:
// - Regtest
// Not valid for:
// - Bitcoin
// - Testnet
// - Signet
let bitcoin_regtest_bech32_address_str = "bcrt1q39c0vrwpgfjkhasu5mfke9wnym45nydfwaeems";
let bitcoin_regtest_bech32_address = Address::new(
bitcoin_regtest_bech32_address_str.to_string(),
Network::Regtest,
)
.unwrap();
assert!(
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Testnet),
"Address should not be valid for Testnet"
);
assert!(
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Signet),
"Address should not be valid for Signet"
);
assert!(
bitcoin_regtest_bech32_address.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
// ====P2PKH====
// | Network | Prefix for P2PKH | Prefix for P2SH |
// |------------------------------------|------------------|-----------------|
// | Bitcoin Mainnet | `1` | `3` |
// | Bitcoin Testnet, Regtest, Signet | `m` or `n` | `2` |
// P2PKH - Bitcoin
// Valid for:
// - Bitcoin
// Not valid for:
// - Testnet
// - Regtest
let bitcoin_mainnet_p2pkh_address_str = "1FfmbHfnpaZjKFvyi1okTjJJusN455paPH";
let bitcoin_mainnet_p2pkh_address = Address::new(
bitcoin_mainnet_p2pkh_address_str.to_string(),
Network::Bitcoin,
)
.unwrap();
assert!(
bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Bitcoin),
"Address should be valid for Bitcoin"
);
assert!(
!bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Testnet),
"Address should not be valid for Testnet"
);
assert!(
!bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Regtest),
"Address should not be valid for Regtest"
);
// P2PKH - Testnet
// Valid for:
// - Testnet
// - Regtest
// Not valid for:
// - Bitcoin
let bitcoin_testnet_p2pkh_address_str = "mucFNhKMYoBQYUAEsrFVscQ1YaFQPekBpg";
let bitcoin_testnet_p2pkh_address = Address::new(
bitcoin_testnet_p2pkh_address_str.to_string(),
Network::Testnet,
)
.unwrap();
assert!(
!bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
// P2PKH - Regtest
// Valid for:
// - Testnet
// - Regtest
// Not valid for:
// - Bitcoin
let bitcoin_regtest_p2pkh_address_str = "msiGFK1PjCk8E6FXeoGkQPTscmcpyBdkgS";
let bitcoin_regtest_p2pkh_address = Address::new(
bitcoin_regtest_p2pkh_address_str.to_string(),
Network::Regtest,
)
.unwrap();
assert!(
!bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
// ====P2SH====
// | Network | Prefix for P2PKH | Prefix for P2SH |
// |------------------------------------|------------------|-----------------|
// | Bitcoin Mainnet | `1` | `3` |
// | Bitcoin Testnet, Regtest, Signet | `m` or `n` | `2` |
// P2SH - Bitcoin
// Valid for:
// - Bitcoin
// Not valid for:
// - Testnet
// - Regtest
let bitcoin_mainnet_p2sh_address_str = "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy";
let bitcoin_mainnet_p2sh_address = Address::new(
bitcoin_mainnet_p2sh_address_str.to_string(),
Network::Bitcoin,
)
.unwrap();
assert!(
bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Bitcoin),
"Address should be valid for Bitcoin"
);
assert!(
!bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Testnet),
"Address should not be valid for Testnet"
);
assert!(
!bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Regtest),
"Address should not be valid for Regtest"
);
// P2SH - Testnet
// Valid for:
// - Testnet
// - Regtest
// Not valid for:
// - Bitcoin
let bitcoin_testnet_p2sh_address_str = "2NFUBBRcTJbYc1D4HSCbJhKZp6YCV4PQFpQ";
let bitcoin_testnet_p2sh_address = Address::new(
bitcoin_testnet_p2sh_address_str.to_string(),
Network::Testnet,
)
.unwrap();
assert!(
!bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
// P2SH - Regtest
// Valid for:
// - Testnet
// - Regtest
// Not valid for:
// - Bitcoin
let bitcoin_regtest_p2sh_address_str = "2NEb8N5B9jhPUCBchz16BB7bkJk8VCZQjf3";
let bitcoin_regtest_p2sh_address = Address::new(
bitcoin_regtest_p2sh_address_str.to_string(),
Network::Regtest,
)
.unwrap();
assert!(
!bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
}
}

View File

@ -3,6 +3,7 @@ package org.bitcoindevkit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.test.assertFalse
class OfflineWalletTest {
@Test
@ -27,6 +28,11 @@ class OfflineWalletTest {
)
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")
assertFalse(addressInfo.address.isValidForNetwork(Network.REGTEST), "Address is valid for regtest network, but it shouldn't be")
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
assertEquals(
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
actual = addressInfo.address.asString()

View File

@ -15,6 +15,11 @@ class TestSimpleWallet(unittest.TestCase):
)
address_info: bdk.AddressInfo = wallet.get_address(bdk.AddressIndex.NEW())
self.assertTrue(address_info.address.is_valid_for_network(bdk.Network.TESTNET), "Address is not valid for testnet network")
self.assertTrue(address_info.address.is_valid_for_network(bdk.Network.SIGNET), "Address is not valid for signet network")
self.assertFalse(address_info.address.is_valid_for_network(bdk.Network.REGTEST), "Address is valid for regtest network, but it shouldn't be")
self.assertFalse(address_info.address.is_valid_for_network(bdk.Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
self.assertEqual("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address_info.address.as_string())
def test_balance(self):

View File

@ -13,7 +13,16 @@ final class OfflineWalletTests: XCTestCase {
network: .testnet
)
let addressInfo: AddressInfo = wallet.getAddress(addressIndex: AddressIndex.new)
XCTAssertTrue(addressInfo.address.isValidForNetwork(network: Network.testnet),
"Address is not valid for testnet network")
XCTAssertTrue(addressInfo.address.isValidForNetwork(network: Network.signet),
"Address is not valid for signet network")
XCTAssertFalse(addressInfo.address.isValidForNetwork(network: Network.regtest),
"Address is valid for regtest network, but it shouldn't be")
XCTAssertFalse(addressInfo.address.isValidForNetwork(network: Network.bitcoin),
"Address is valid for bitcoin network, but it shouldn't be")
XCTAssertEqual(addressInfo.address.asString(), "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e")
}