Сабж. Около ляма адресов,нужно чекнуть на наличие баланса Bitcoin Cash ,
Ищу решение. Велкам в жаббер или ПМ
Ищу решение. Велкам в жаббер или ПМ
#!/usr/bin/env python3
"""
Bitcoin Cash (BCH) Bulk Address Checker with Balance
---------------------------------------------------
This script validates multiple Bitcoin Cash addresses at once and checks their balances.
It supports Legacy, CashAddr, and SLP address formats.
Usage:
1. Run the script
2. Enter addresses or specify an input file
3. View validation results and balances
"""
import re
import sys
import json
import argparse
import time
import requests
from typing import Dict, List, Optional, Tuple
class BCHAddressChecker:
def __init__(self, api="blockchair"):
"""
Initialize the BCH address checker with the specified API.
Args:
api: API provider to use ('blockchair' or 'blockchain')
"""
# API selection for querying BCH address data
self.api = api.lower()
# Regular expression patterns for different BCH address formats
self.legacy_pattern = re.compile(r'^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$')
self.cashaddr_pattern = re.compile(r'^(bitcoincash:)?[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{42}$')
self.slp_pattern = re.compile(r'^(simpleledger:)?[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{42}$')
def validate_address(self, address: str) -> Optional[Dict]:
"""
Validate a single BCH address and determine its format.
Args:
address: The BCH address to validate
Returns:
Dictionary with validation results or None if empty address
"""
# Remove whitespace and normalize
address = address.strip()
# Skip empty lines
if not address:
return None
# Remove prefix for pattern matching if present
address_without_prefix = address
if address.startswith('bitcoincash:'):
address_without_prefix = address[12:]
elif address.startswith('simpleledger:'):
address_without_prefix = address[13:]
# Check against patterns
is_legacy_valid = bool(self.legacy_pattern.match(address))
is_cashaddr_valid = (
bool(self.cashaddr_pattern.match(address)) or
bool(self.cashaddr_pattern.match(f'bitcoincash:{address_without_prefix}'))
)
is_slp_valid = (
bool(self.slp_pattern.match(address)) or
bool(self.slp_pattern.match(f'simpleledger:{address_without_prefix}'))
)
is_valid = is_legacy_valid or is_cashaddr_valid or is_slp_valid
# Determine format type
format_type = 'Unknown'
if is_legacy_valid:
format_type = 'Legacy'
elif is_cashaddr_valid:
format_type = 'CashAddr'
elif is_slp_valid:
format_type = 'SLP'
return {
'address': address,
'is_valid': is_valid,
'format': format_type if is_valid else 'Invalid',
'balance': None # Will be populated later
}
def get_address_balance(self, address: str) -> float:
"""
Fetch the balance of a BCH address from the blockchain explorer API.
Args:
address: The BCH address to check
Returns:
Balance in BCH as a float, or None if there was an error
"""
try:
# Ensure the address has the correct format for the API
if address.startswith('bitcoincash:'):
formatted_address = address
elif address.startswith('simpleledger:'):
# Convert SLP to regular CashAddr for balance check
formatted_address = f"bitcoincash:{address[13:]}"
elif self.legacy_pattern.match(address):
# For legacy addresses, use as is
formatted_address = address
else:
# For CashAddr without prefix, add the prefix
formatted_address = f"bitcoincash:{address}"
if self.api == 'blockchair':
# Use Blockchair API to get address details
url = f"https://api.blockchair.com/bitcoin-cash/dashboards/address/{formatted_address}"
response = requests.get(url)
if response.status_code == 200:
data = response.json()
if data.get('context', {}).get('code') == 200:
# Get the address data
address_data = data.get('data', {}).get(formatted_address, {})
# Balance is returned in satoshis, convert to BCH
balance_sats = address_data.get('address', {}).get('balance', 0)
return balance_sats / 100000000 # Convert satoshis to BCH
else:
print(f"API Error: {data.get('context', {}).get('error', 'Unknown error')}")
return None
else:
print(f"API Error ({response.status_code}): {response.text}")
return None
elif self.api == 'blockchain':
# Use Blockchain.com API to get address details
url = f"https://blockchain.info/rawaddr/{formatted_address}?coin=bch"
response = requests.get(url)
if response.status_code == 200:
data = response.json()
# Balance is returned in satoshis, convert to BCH
balance_sats = data.get('final_balance', 0)
return balance_sats / 100000000 # Convert satoshis to BCH
else:
print(f"API Error ({response.status_code}): {response.text}")
return None
else:
print(f"Unknown API provider: {self.api}")
return None
except Exception as e:
print(f"Error fetching balance for {address}: {e}")
return None
def batch_get_balances(self, addresses: List[str], batch_size=5) -> Dict[str, float]:
"""
Fetch balances for multiple addresses in batches to avoid rate limiting.
Args:
addresses: List of BCH addresses
batch_size: Number of addresses to process in each batch
Returns:
Dictionary mapping addresses to their balances
"""
balances = {}
total_addresses = len(addresses)
for i in range(0, total_addresses, batch_size):
batch = addresses[i:i+batch_size]
print(f"Fetching balances for addresses {i+1}-{min(i+batch_size, total_addresses)} of {total_addresses}...")
for address in batch:
retries = 3
while retries > 0:
try:
balance = self.get_address_balance(address)
balances[address] = balance
# Add a delay to avoid rate limiting
time.sleep(1) # Increased delay for more reliable API responses
break
except requests.exceptions.RequestException:
print(f"Network error, retrying... ({retries} attempts left)")
retries -= 1
time.sleep(2) # Wait longer between retries
if retries == 0:
print(f"Failed to fetch balance for {address} after multiple attempts.")
balances[address] = None
return balances
def validate_addresses(self, addresses: List[str], check_balance=True, batch_size=5) -> List[Dict]:
"""
Validate multiple BCH addresses and optionally check their balances.
Args:
addresses: List of BCH addresses to validate
check_balance: Whether to fetch balances from the blockchain
batch_size: Number of addresses to process in each batch
Returns:
List of dictionaries with validation results and balances
"""
# First validate all addresses
results = []
valid_addresses = []
for address in addresses:
result = self.validate_address(address)
if result:
results.append(result)
if result['is_valid']:
valid_addresses.append(result['address'])
# Then fetch balances for valid addresses if requested
if check_balance and valid_addresses:
balances = self.batch_get_balances(valid_addresses, batch_size)
# Update results with balance information
for result in results:
if result['is_valid']:
result['balance'] = balances.get(result['address'])
return results
def print_results(self, results: List[Dict]) -> None:
"""
Print validation results and balances in a formatted table.
Args:
results: List of validation result dictionaries
"""
# Count valid and invalid addresses
valid_count = sum(1 for result in results if result['is_valid'])
invalid_count = len(results) - valid_count
# Calculate total balance
total_balance = sum(result['balance'] or 0 for result in results if result['is_valid'] and result['balance'] is not None)
# Print statistics
print("\n===== RESULTS =====")
print(f"Total addresses: {len(results)}")
print(f"Valid addresses: {valid_count}")
print(f"Invalid addresses: {invalid_count}")
print(f"Total balance: {total_balance:.8f} BCH")
print("===================")
# Print table header
if any(result['balance'] is not None for result in results):
print("\n{:<45} {:<10} {:<12} {:<15}".format("ADDRESS", "STATUS", "FORMAT", "BALANCE (BCH)"))
print("="*85)
# Print each result
for result in results:
status = "Valid" if result['is_valid'] else "Invalid"
balance = f"{result['balance']:.8f}" if result['balance'] is not None else "N/A"
print("{:<45} {:<10} {:<12} {:<15}".format(
result['address'][:43] + '...' if len(result['address']) > 45 else result['address'],
status,
result['format'],
balance if result['is_valid'] else "—"
))
else:
# Print table without balance column if no balances were fetched
print("\n{:<50} {:<10} {:<10}".format("ADDRESS", "STATUS", "FORMAT"))
print("="*70)
for result in results:
status = "Valid" if result['is_valid'] else "Invalid"
print("{:<50} {:<10} {:<10}".format(
result['address'][:48] + '...' if len(result['address']) > 50 else result['address'],
status,
result['format']
))
def read_addresses_from_file(file_path: str) -> List[str]:
"""
Read BCH addresses from a file, one address per line.
Args:
file_path: Path to the file containing addresses
Returns:
List of addresses
"""
try:
with open(file_path, 'r') as file:
return [line.strip() for line in file.readlines()]
except Exception as e:
print(f"Error reading file: {e}")
return []
def main() -> None:
"""Main function to run the BCH address checker with balance."""
parser = argparse.ArgumentParser(description='Bitcoin Cash (BCH) Bulk Address Checker with Balance')
parser.add_argument('-f', '--file', help='Path to file containing BCH addresses (one per line)')
parser.add_argument('-a', '--addresses', help='Comma-separated list of BCH addresses')
parser.add_argument('--no-balance', action='store_true', help='Skip balance checking')
parser.add_argument('--api', choices=['blockchair', 'blockchain'], default='blockchair',
help='API provider to use (blockchair or blockchain)')
parser.add_argument('--batch-size', type=int, default=5, help='Number of addresses to process in each batch')
args = parser.parse_args()
print(f"Using {args.api} API for balance checking")
checker = BCHAddressChecker(api=args.api)
addresses = []
# Get addresses from file, command-line arguments, or user input
if args.file:
addresses = read_addresses_from_file(args.file)
elif args.addresses:
addresses = [addr.strip() for addr in args.addresses.split(',')]
else:
print("Enter BCH addresses (one per line, press Ctrl+D or Ctrl+Z when done):")
try:
addresses = [line.strip() for line in sys.stdin.readlines()]
except KeyboardInterrupt:
print("\nInput interrupted.")
return
# Validate addresses and check balances
if addresses:
print(f"Processing {len(addresses)} addresses...")
results = checker.validate_addresses(
addresses,
check_balance=not args.no_balance,
batch_size=args.batch_size
)
checker.print_results(results)
else:
print("No addresses provided. Exiting.")
if __name__ == "__main__":
main()