use crate::secondary_validation::Validator;
use bitcoin::{Address, Network};
use std::str::FromStr;
pub struct BtcChecksum;
impl Validator for BtcChecksum {
fn is_valid_match(&self, regex_match: &str) -> bool {
let clean_input = regex_match
.chars()
.filter(|c| c.is_alphanumeric())
.collect::<String>();
let address = Address::from_str(&clean_input);
address.is_ok_and(|addr| addr.is_valid_for_network(Network::Bitcoin))
}
}
#[cfg(test)]
mod test {
use crate::secondary_validation::*;
#[test]
fn test_valid_bitcoin_addresses() {
let valid_addresses = vec![
"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2",
"12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
"17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j",
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
"3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy",
"3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC",
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
"bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3",
"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",
];
for address in valid_addresses {
assert!(
BtcChecksum.is_valid_match(address),
"Failed for address: {address}"
);
}
}
#[test]
fn test_invalid_bitcoin_addresses() {
let invalid_addresses = vec![
"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNb",
"1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN3",
"1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz",
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",
"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfN0", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNO", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNI", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNl", "17NdbrSGoUotzeGCcMMC?nFkEvLymoou9j", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv4", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3tb", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3tO", "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7Kv8f3t4", "1",
"12",
"bc1",
];
for address in invalid_addresses {
assert!(
!BtcChecksum.is_valid_match(address),
"Should be invalid: {address}"
);
}
}
#[test]
fn test_addresses_with_whitespace() {
assert!(BtcChecksum.is_valid_match(" 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa "));
assert!(BtcChecksum.is_valid_match("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa\n"));
assert!(BtcChecksum.is_valid_match("\t1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa\t"));
assert!(BtcChecksum.is_valid_match(" bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 "));
assert!(BtcChecksum.is_valid_match("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\n"));
}
#[test]
fn test_bech32_specific_validation() {
assert!(BtcChecksum.is_valid_match("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"));
assert!(BtcChecksum.is_valid_match("BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4")); assert!(BtcChecksum.is_valid_match("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"));
assert!(
BtcChecksum
.is_valid_match("bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0")
);
assert!(!BtcChecksum.is_valid_match("tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx"));
assert!(!BtcChecksum.is_valid_match("bc1QW508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"));
assert!(!BtcChecksum.is_valid_match("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"));
assert!(!BtcChecksum.is_valid_match("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3tb"));
}
}