dd_sds/secondary_validation/
btc_checksum.rs1use crate::secondary_validation::Validator;
2use bitcoin::{Address, Network};
3use std::str::FromStr;
4
5pub struct BtcChecksum;
6
7impl Validator for BtcChecksum {
8 fn is_valid_match(&self, regex_match: &str) -> bool {
9 let clean_input = regex_match
11 .chars()
12 .filter(|c| c.is_alphanumeric())
13 .collect::<String>();
14
15 let address = Address::from_str(&clean_input);
16 address.is_ok_and(|addr| addr.is_valid_for_network(Network::Bitcoin))
17 }
18}
19
20#[cfg(test)]
21mod test {
22 use crate::secondary_validation::*;
23
24 #[test]
25 fn test_valid_bitcoin_addresses() {
26 let valid_addresses = vec![
27 "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
29 "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2",
30 "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S",
31 "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
32 "17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j",
33 "1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
34 "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy",
36 "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC",
37 "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
39 "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3",
40 "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",
42 ];
43 for address in valid_addresses {
44 assert!(
45 BtcChecksum.is_valid_match(address),
46 "Failed for address: {address}"
47 );
48 }
49 }
50
51 #[test]
52 fn test_invalid_bitcoin_addresses() {
53 let invalid_addresses = vec![
54 "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNb",
56 "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN3",
57 "1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i",
58 "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",
59 "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",
60 "1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
61 "1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
62 "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz",
63 "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz",
64 "1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",
65 "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",
66 "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfN0", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNO", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNI", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNl", "17NdbrSGoUotzeGCcMMC?nFkEvLymoou9j", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv4", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3tb", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3tO", "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7Kv8f3t4", "1",
82 "12",
83 "bc1",
84 ];
85 for address in invalid_addresses {
86 assert!(
87 !BtcChecksum.is_valid_match(address),
88 "Should be invalid: {address}"
89 );
90 }
91 }
92
93 #[test]
94 fn test_addresses_with_whitespace() {
95 assert!(BtcChecksum.is_valid_match(" 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa "));
97 assert!(BtcChecksum.is_valid_match("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa\n"));
98 assert!(BtcChecksum.is_valid_match("\t1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa\t"));
99
100 assert!(BtcChecksum.is_valid_match(" bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 "));
102 assert!(BtcChecksum.is_valid_match("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\n"));
103 }
104
105 #[test]
106 fn test_bech32_specific_validation() {
107 assert!(BtcChecksum.is_valid_match("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"));
109 assert!(BtcChecksum.is_valid_match("BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4")); assert!(BtcChecksum.is_valid_match("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")); assert!(
114 BtcChecksum
115 .is_valid_match("bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0")
116 );
117
118 assert!(!BtcChecksum.is_valid_match("tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx"));
120
121 assert!(!BtcChecksum.is_valid_match("bc1QW508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"));
123
124 assert!(!BtcChecksum.is_valid_match("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"));
126
127 assert!(!BtcChecksum.is_valid_match("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3tb"));
129 }
130}