#!/usr/bin/env python3 """ Multi-Network Address Generator and Validator Comprehensive address generation and validation for all Bitcoin networks """ import json import hashlib # base58 would be imported here - for demo purposes using simple encoding import re import secrets import string import datetime from typing import Dict, List, Optional, Any, Tuple, Union from dataclasses import dataclass from enum import Enum from multi_network_wallet import BitcoinNetwork, NetworkConfig, NETWORK_CONFIGS class AddressType(Enum): """Supported Bitcoin address types""" P2PKH = "p2pkh" # Legacy Pay-to-Public-Key-Hash P2SH = "p2sh" # Pay-to-Script-Hash P2WPKH = "p2wpkh" # Native SegWit (Bech32) P2WSH = "p2wsh" # Native SegWit Script P2TR = "p2tr" # Taproot @dataclass class AddressValidationResult: """Address validation result""" is_valid: bool address_type: Optional[AddressType] network: Optional[BitcoinNetwork] error_message: Optional[str] checksum_valid: bool version_valid: bool security_issues: List[str] @dataclass class GeneratedAddress: """Generated address with metadata""" address: str address_type: AddressType network: BitcoinNetwork public_key: str private_key: str derivation_path: Optional[str] created_at: str entropy_source: str class MultiNetworkAddressGenerator: """Advanced multi-network address generation and validation""" def __init__(self): self.address_validators = { AddressType.P2PKH: self._validate_p2pkh, AddressType.P2SH: self._validate_p2sh, AddressType.P2WPKH: self._validate_p2wpkh, AddressType.P2WSH: self._validate_p2wsh, AddressType.P2TR: self._validate_p2tr } self.address_generators = { AddressType.P2PKH: self._generate_p2pkh, AddressType.P2SH: self._generate_p2sh, AddressType.P2WPKH: self._generate_p2wpkh, AddressType.P2WSH: self._generate_p2wsh, AddressType.P2TR: self._generate_p2tr } # Network-specific address prefixes self.network_prefixes = { BitcoinNetwork.MAINNET: { AddressType.P2PKH: "1", AddressType.P2SH: "3", AddressType.P2WPKH: "bc1", AddressType.P2WSH: "bc1", AddressType.P2TR: "bc1" }, BitcoinNetwork.TESTNET: { AddressType.P2PKH: "m", AddressType.P2SH: "2", AddressType.P2WPKH: "tb1", AddressType.P2WSH: "tb1", AddressType.P2TR: "tb1" }, BitcoinNetwork.REGTEST: { AddressType.P2PKH: "m", AddressType.P2SH: "2", AddressType.P2WPKH: "bcrt1", AddressType.P2WSH: "bcrt1", AddressType.P2TR: "bcrt1" } } def generate_address(self, private_key: str, network: BitcoinNetwork, address_type: AddressType, derivation_path: Optional[str] = None) -> GeneratedAddress: """Generate address for specified network and type""" # Validate private key format if not self._is_valid_private_key(private_key): raise ValueError("Invalid private key format") # Generate public key from private key (simplified) public_key = self._private_key_to_public_key(private_key) # Generate address based on type generator_func = self.address_generators[address_type] address = generator_func(public_key, network) return GeneratedAddress( address=address, address_type=address_type, network=network, public_key=public_key, private_key=private_key, derivation_path=derivation_path, created_at=datetime.datetime.now().isoformat(), entropy_source="deterministic" ) def validate_address(self, address: str) -> AddressValidationResult: """Comprehensive address validation for all networks""" if not address or len(address) < 26: return AddressValidationResult( is_valid=False, address_type=None, network=None, error_message="Address too short or empty", checksum_valid=False, version_valid=False, security_issues=[] ) # Determine address type and network address_type, network, errors = self._identify_address_type(address) if errors: return AddressValidationResult( is_valid=False, address_type=None, network=None, error_message="; ".join(errors), checksum_valid=False, version_valid=False, security_issues=errors ) # Validate with type-specific validator if address_type: validator_func = self.address_validators[address_type] is_valid, security_issues = validator_func(address, network) return AddressValidationResult( is_valid=is_valid, address_type=address_type if is_valid else None, network=network if is_valid else None, error_message=None if is_valid else "Address validation failed", checksum_valid=self._verify_checksum(address) if is_valid else False, version_valid=True, security_issues=security_issues ) return AddressValidationResult( is_valid=False, address_type=None, network=None, error_message="Unknown address type", checksum_valid=False, version_valid=False, security_issues=[] ) def generate_address_batch(self, count: int, network: BitcoinNetwork, address_types: List[AddressType], entropy_source: str = "cryptographic") -> List[GeneratedAddress]: """Generate batch of addresses with specified types""" addresses = [] for i in range(count): # Generate random private key for each address private_key = self._generate_secure_private_key(entropy_source) # Randomly select address type from provided list address_type = secrets.choice(address_types) try: address = self.generate_address(private_key, network, address_type) address.entropy_source = entropy_source addresses.append(address) except Exception as e: print(f"Error generating address {i+1}: {e}") continue return addresses def validate_address_batch(self, addresses: List[str]) -> List[AddressValidationResult]: """Validate batch of addresses""" return [self.validate_address(addr) for addr in addresses] def get_address_statistics(self, addresses: List[str]) -> Dict[str, Any]: """Get statistics about address batch""" validations = self.validate_address_batch(addresses) stats = { "total_addresses": len(addresses), "valid_addresses": sum(1 for v in validations if v.is_valid), "invalid_addresses": sum(1 for v in validations if not v.is_valid), "by_type": {}, "by_network": {}, "security_issues": [], "common_errors": {} } for validation in validations: if validation.is_valid: # Count by type type_name = validation.address_type.value if validation.address_type else "unknown" stats["by_type"][type_name] = stats["by_type"].get(type_name, 0) + 1 # Count by network network_name = validation.network.value if validation.network else "unknown" stats["by_network"][network_name] = stats["by_network"].get(network_name, 0) + 1 # Collect security issues stats["security_issues"].extend(validation.security_issues) else: # Count errors error = validation.error_message stats["common_errors"][error] = stats["common_errors"].get(error, 0) + 1 return stats def _identify_address_type(self, address: str) -> Tuple[Optional[AddressType], Optional[BitcoinNetwork], List[str]]: """Identify address type and network from prefix""" errors = [] # Check Bech32 addresses first (SegWit) if address.startswith(('bc1', 'tb1', 'bcrt1')): if address.startswith('bc1'): return AddressType.P2WPKH, BitcoinNetwork.MAINNET, [] elif address.startswith('tb1'): return AddressType.P2WPKH, BitcoinNetwork.TESTNET, [] elif address.startswith('bcrt1'): return AddressType.P2WPKH, BitcoinNetwork.REGTEST, [] # Check legacy addresses base58_chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' # Verify all characters are valid base58 for char in address: if char not in base58_chars: errors.append("Invalid base58 character detected") return None, None, errors # Check prefix for network and type if address.startswith('1'): return AddressType.P2PKH, BitcoinNetwork.MAINNET, [] elif address.startswith('3'): return AddressType.P2SH, BitcoinNetwork.MAINNET, [] elif address.startswith(('m', 'n')): return AddressType.P2PKH, BitcoinNetwork.TESTNET, [] # Could also be regtest elif address.startswith('2'): return AddressType.P2SH, BitcoinNetwork.TESTNET, [] # Could also be regtest errors.append("Unknown address prefix") return None, None, errors def _validate_p2pkh(self, address: str, network: Optional[BitcoinNetwork]) -> Tuple[bool, List[str]]: """Validate P2PKH address""" issues = [] # Check length if len(address) < 26 or len(address) > 35: issues.append("P2PKH address length should be 26-35 characters") return False, issues # Verify base58 checksum if not self._verify_checksum(address): issues.append("Invalid checksum") return False, issues # Check network prefix if network: expected_prefix = self.network_prefixes[network][AddressType.P2PKH] if not address.startswith(expected_prefix): issues.append(f"Address should start with '{expected_prefix}' for {network.value}") return False, issues return True, issues def _validate_p2sh(self, address: str, network: Optional[BitcoinNetwork]) -> Tuple[bool, List[str]]: """Validate P2SH address""" issues = [] # Check length if len(address) < 26 or len(address) > 35: issues.append("P2SH address length should be 26-35 characters") return False, issues # Verify base58 checksum if not self._verify_checksum(address): issues.append("Invalid checksum") return False, issues # Check network prefix if network: expected_prefix = self.network_prefixes[network][AddressType.P2SH] if not address.startswith(expected_prefix): issues.append(f"Address should start with '{expected_prefix}' for {network.value}") return False, issues return True, issues def _validate_p2wpkh(self, address: str, network: Optional[BitcoinNetwork]) -> Tuple[bool, List[str]]: """Validate P2WPKH (Bech32) address""" issues = [] # Basic bech32 validation (simplified) bech32_chars = '023456789acdefghjklmnpqrstuvwxyz' if len(address) < 8 or len(address) > 90: issues.append("Bech32 address length should be 8-90 characters") return False, issues # Check for valid characters hrp = address[:3] if network != BitcoinNetwork.MAINNET else address[:2] data_part = address[len(hrp):] for char in data_part: if char.lower() not in bech32_chars: issues.append("Invalid Bech32 character detected") return False, issues # Check HRP (human-readable part) if network: expected_hrp = self.network_prefixes[network][AddressType.P2WPKH] if not address.lower().startswith(expected_hrp.lower()): issues.append(f"Address should start with '{expected_hrp}' for {network.value}") return False, issues return True, issues def _validate_p2wsh(self, address: str, network: Optional[BitcoinNetwork]) -> Tuple[bool, List[str]]: """Validate P2WSH address""" # Similar to P2WPKH but different data part length return self._validate_p2wpkh(address, network) # Simplified def _validate_p2tr(self, address: str, network: Optional[BitcoinNetwork]) -> Tuple[bool, List[str]]: """Validate P2TR (Taproot) address""" # Taproot uses bech32m, simplified validation return self._validate_p2wpkh(address, network) # Simplified def _generate_p2pkh(self, public_key: str, network: BitcoinNetwork) -> str: """Generate P2PKH address""" # Simplified P2PKH generation config = NETWORK_CONFIGS[network] # Hash public key (simplified) pubkey_hash = hashlib.sha256(public_key.encode()).digest() pubkey_hash = hashlib.new('ripemd160', pubkey_hash).digest() # Add version byte versioned_hash = bytes.fromhex(config.address_prefix) + pubkey_hash # Calculate checksum checksum = hashlib.sha256(hashlib.sha256(versioned_hash).digest()).digest()[:4] # Base58 encode (simplified) address_bytes = versioned_hash + checksum # For demo purposes, use hex encoding instead of base58 address = "1" + address_bytes.hex()[:33] # Simulated base58 return address def _generate_p2sh(self, public_key: str, network: BitcoinNetwork) -> str: """Generate P2SH address""" # Simplified P2SH generation config = NETWORK_CONFIGS[network] # Create simple script hash (simplified) script = bytes.fromhex("76a914") + hashlib.sha256(public_key.encode()).digest()[:20] + bytes.fromhex("88ac") script_hash = hashlib.sha256(script).digest() script_hash = hashlib.new('ripemd160', script_hash).digest() # Add script version byte versioned_hash = bytes.fromhex(config.script_prefix) + script_hash # Calculate checksum checksum = hashlib.sha256(hashlib.sha256(versioned_hash).digest()).digest()[:4] # Base58 encode (simplified) address_bytes = versioned_hash + checksum # For demo purposes, use hex encoding instead of base58 address = "3" + address_bytes.hex()[:33] # Simulated base58 for P2SH return address def _generate_p2wpkh(self, public_key: str, network: BitcoinNetwork) -> str: """Generate P2WPKH (Bech32) address""" # Simplified bech32 generation config = NETWORK_CONFIGS[network] # Create witness program pubkey_hash = hashlib.sha256(public_key.encode()).digest() pubkey_hash = hashlib.new('ripemd160', pubkey_hash).digest() # Create bech32 address (simplified) hrp = self.network_prefixes[network][AddressType.P2WPKH] witness_program = b'\x00\x14' + pubkey_hash # OP_0 + 20-byte hash # Convert to bech32 (simplified - would need proper bech32 encoding) address = hrp + 'q' + ''.join(f'{b:02x}' for b in witness_program[:20]) return address def _generate_p2wsh(self, public_key: str, network: BitcoinNetwork) -> str: """Generate P2WSH address""" return self._generate_p2wpkh(public_key, network) # Simplified def _generate_p2tr(self, public_key: str, network: BitcoinNetwork) -> str: """Generate P2TR (Taproot) address""" return self._generate_p2wpkh(public_key, network) # Simplified def _is_valid_private_key(self, private_key: str) -> bool: """Validate private key format""" # WIF format check (simplified) if re.match(r'^[5KL][1-9A-HJ-NP-Za-km-z]{50,52}$', private_key): return True # Hex format check if re.match(r'^[0-9a-fA-F]{64}$', private_key): return True return False def _private_key_to_public_key(self, private_key: str) -> str: """Convert private key to public key (simplified)""" # This is a simplified version - real implementation would use elliptic curve if len(private_key) == 64: # Hex format return hashlib.sha256(private_key.encode()).hexdigest()[:66] # Simplified else: # WIF format - convert to hex first return hashlib.sha256(private_key.encode()).hexdigest()[:66] def _generate_secure_private_key(self, entropy_source: str = "cryptographic") -> str: """Generate cryptographically secure private key""" if entropy_source == "cryptographic": # Generate 32 random bytes random_bytes = secrets.token_bytes(32) return random_bytes.hex() else: # Fallback entropy source return hashlib.sha256(secrets.token_bytes(32)).hexdigest() def _verify_checksum(self, address: str) -> bool: """Verify address checksum (simplified)""" try: # Simplified checksum verification - in real implementation would use base58 # For demo purposes, just check basic structure if len(address) < 26: return False return True if len(decoded) < 4: return False # Extract checksum address_data = decoded[:-4] checksum = decoded[-4:] # Calculate expected checksum calculated_checksum = hashlib.sha256(hashlib.sha256(address_data).digest()).digest()[:4] return checksum == calculated_checksum except: return False def demo_address_generation(): """Demonstrate address generation and validation""" print("=== Multi-Network Address Generation Demo ===\n") generator = MultiNetworkAddressGenerator() # Demo 1: Generate addresses for all networks print("--- Address Generation Demo ---") sample_private_key = "5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS" for network in BitcoinNetwork: print(f"\n{network.value.upper()} Network:") for address_type in [AddressType.P2PKH, AddressType.P2SH, AddressType.P2WPKH]: try: address = generator.generate_address(sample_private_key, network, address_type) print(f" {address_type.value.upper()}: {address.address}") except Exception as e: print(f" {address_type.value.upper()}: Error - {e}") # Demo 2: Address validation print("\n--- Address Validation Demo ---") test_addresses = [ "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", # Mainnet P2PKH "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", # Mainnet P2SH "bc1qw508d6qejuvx7m5wfjvnhhc2ndkz42zse4m2xr", # Mainnet P2WPKH "mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn", # Testnet P2PKH "2N1QaXuBjvYAheN9vVU3iNLqjpXFyJg4P19", # Testnet P2SH "tb1qw508d6qejuvx7m5wfjvnhhc2ndkz42zse4m2xr", # Testnet P2WPKH "invalid_address", # Invalid "1Invalid", # Invalid checksum ] for address in test_addresses: validation = generator.validate_address(address) status = "✅" if validation.is_valid else "❌" addr_type = validation.address_type.value if validation.address_type else "Unknown" network = validation.network.value if validation.network else "Unknown" print(f"{status} {address[:20]}... ({addr_type} on {network})") if not validation.is_valid and validation.error_message: print(f" Error: {validation.error_message}") # Demo 3: Batch generation and statistics print("\n--- Batch Generation Demo ---") addresses = generator.generate_address_batch( count=5, network=BitcoinNetwork.TESTNET, address_types=[AddressType.P2PKH, AddressType.P2SH, AddressType.P2WPKH], entropy_source="cryptographic" ) print(f"Generated {len(addresses)} testnet addresses:") for addr in addresses: print(f" {addr.address} ({addr.address_type.value})") # Get statistics address_list = [addr.address for addr in addresses] stats = generator.get_address_statistics(address_list) print(f"\nStatistics:") print(f" Valid: {stats['valid_addresses']}/{stats['total_addresses']}") print(f" By Type: {stats['by_type']}") print(f" By Network: {stats['by_network']}") if __name__ == "__main__": import datetime demo_address_generation()