bitcoinlibで遊んでみた

GPT先生にbitcoinlibでビットコインのCLIウォレットを構築してと頼んでみました。

https://bitcoinlib.readthedocs.io/en/latest/source/_static/manuals.command-line-wallet.html#create-wallet

bitcoinlibで全て出来るのでわざわざこんなコードを書く必要はないんですが、ドキュメント読み込むより実際に動くコード見せてもらった方が理解が早いので、何度か先生にお願いして基本的な機能を実装したウォレットを作ってもらいました。

```python
import os
import sqlite3
import pyqrcode
from bitcoinlib.wallets import Wallet, wallet_delete
from bitcoinlib.keys import Address
from bitcoinlib.services.services import Service

def create_wallet(wallet_name, wallet_type='p2pkh'):
    network = 'testnet'
    if wallet_type == 'p2pkh':
        wallet = Wallet.create(wallet_name, network=network)
    elif wallet_type == 'segwit':
        wallet = Wallet.create(wallet_name, network=network, witness_type='segwit')
    else:
        print(f"Unsupported wallet type: {wallet_type}")
        return None
    
    key = wallet.get_key()
    address = key.address
    wif_key = key.wif
    with sqlite3.connect("wallet_metadata.db") as conn:
        cursor = conn.cursor()
        cursor.execute("CREATE TABLE IF NOT EXISTS wallet_info (name TEXT, address_type TEXT)")
        cursor.execute("INSERT INTO wallet_info (name, address_type) VALUES (?, ?)", (wallet_name, wallet_type))
        conn.commit()
    
    print(f"Wallet '{wallet_name}' of type '{wallet_type}' created.")
    print(f"Address: {address}")
    print(f"Key (WIF): {wif_key}")
    return wallet

def check_balance(wallet_name):
    try:
        wallet = Wallet(wallet_name)
        address = wallet.get_key().address
        svc = Service(network='testnet')
        balance = svc.getbalance(address)
        print(f"Balance for wallet '{wallet_name}' (address: {address}): {balance} BTC")
        return balance
    except Exception as e:
        print(f"Error checking balance for wallet '{wallet_name}': {e}")
        return None

def send_bitcoin(wallet_name, recipient_address, amount):
    if amount <= 0:
        print("Amount must be greater than 0.")
        return None
    
    wallet = Wallet(wallet_name)
    try:
        tx = wallet.send_to(recipient_address, amount)
        print(f"Transaction sent. TXID: {tx.txid}")
        return tx.txid
    except Exception as e:
        print(f"Error sending Bitcoin: {e}")
        return None

def send_bitcoin_multiple(wallet_name, recipients):
    if not recipients:
        print("No recipients provided.")
        return None

    wallet = Wallet(wallet_name)
    outputs = [(address, amount) for address, amount in recipients if amount > 0]

    if not outputs:
        print("All amounts must be greater than 0.")
        return None

    try:
        tx = wallet.transaction_create(outputs)
        tx.sign()
        tx.send()
        print(f"Transaction sent. TXID: {tx.txid}")
        return tx.txid
    except Exception as e:
        print(f"Error sending Bitcoin: {e}")
        return None

def list_wallets():
    db_path = os.path.expanduser("~/.bitcoinlib/database/bitcoinlib.sqlite")
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    cursor.execute("SELECT name FROM wallets")
    wallets = cursor.fetchall()
    conn.close()

    with sqlite3.connect("wallet_metadata.db") as conn:
        cursor = conn.cursor()
        cursor.execute("CREATE TABLE IF NOT EXISTS wallet_info (name TEXT, address_type TEXT)")
        cursor.execute("SELECT name, address_type FROM wallet_info")
        wallet_info = cursor.fetchall()

    if not wallets:
        print("No wallets found.")
    else:
        for wallet in wallets:
            name = wallet[0]
            address_type = next((wt[1] for wt in wallet_info if wt[0] == name), "unknown")
            print(f"Wallet: {name}, Type: {address_type}")

def delete_wallet(wallet_name):
    wallet_delete(wallet_name)
    with sqlite3.connect("wallet_metadata.db") as conn:
        cursor = conn.cursor()
        cursor.execute("DELETE FROM wallet_info WHERE name = ?", (wallet_name,))
        conn.commit()
    print(f"Wallet '{wallet_name}' deleted.")

def view_keys(wallet_name):
    try:
        wallet = Wallet(wallet_name)
        key = wallet.get_key()
        wif_key = key.wif
        print(f"Wallet '{wallet_name}' key:")
        print(f"Key (WIF): {wif_key}")
    except Exception as e:
        print(f"Error retrieving keys for wallet '{wallet_name}': {e}")

def view_address(wallet_name):
    try:
        wallet = Wallet(wallet_name)
        key = wallet.get_key()
        address = key.address
        print(f"Wallet '{wallet_name}' address:")
        print(f"Address: {address}")
        qr = pyqrcode.create(address)
        qr.png("qrcode.png", scale=8)
        os.system("xdg-open qrcode.png")
    except Exception as e:
        print(f"Error retrieving address for wallet '{wallet_name}': {e}")

def is_valid_address(address):
    return len(address) > 0 and (address.startswith('1') or address.startswith('3') or address.startswith('m') or address.startswith('2') or address.startswith('tb1') or address.startswith('bcrt1'))

def get_valid_amount():
    while True:
        try:
            amount = float(input("Enter amount to send (BTC): "))
            if amount <= 0:
                raise ValueError("Amount must be greater than 0.")
            return amount
        except ValueError as e:
            print(f"Invalid amount: {e}")

def main():
    print("Bitcoin Wallet Application (Testnet)")
    print("====================================")
    print("1. Create Wallet")
    print("2. Check Balance")
    print("3. Send Bitcoin")
    print("4. Send Bitcoin to Multiple Addresses")
    print("5. List Wallets")
    print("6. Delete Wallet")
    print("7. View Wallet Keys")
    print("8. View Wallet Address and QR Code")
    print("9. Exit")

    while True:
        choice = input("Enter your choice: ")

        if choice == '1':
            wallet_name = input("Enter wallet name: ")
            wallet_type = input("Enter wallet type (p2pkh, segwit): ").strip().lower()
            create_wallet(wallet_name, wallet_type)
        elif choice == '2':
            wallet_name = input("Enter wallet name: ")
            check_balance(wallet_name)
        elif choice == '3':
            wallet_name = input("Enter wallet name: ")
            recipient_address = input("Enter recipient address: ")
            if not is_valid_address(recipient_address):
                print("Invalid recipient address.")
                continue
            amount = get_valid_amount()
            send_bitcoin(wallet_name, recipient_address, amount)
        elif choice == '4':
            wallet_name = input("Enter wallet name: ")
            recipients = []
            while True:
                recipient_address = input("Enter recipient address (or press Enter to finish): ")
                if not recipient_address:
                    break
                if not is_valid_address(recipient_address):
                    print("Invalid recipient address.")
                    continue
                amount = get_valid_amount()
                recipients.append((recipient_address, amount))
            send_bitcoin_multiple(wallet_name, recipients)
        elif choice == '5':
            list_wallets()
        elif choice == '6':
            wallet_name = input("Enter wallet name to delete: ")
            delete_wallet(wallet_name)
        elif choice == '7':
            wallet_name = input("Enter wallet name: ")
            view_keys(wallet_name)
        elif choice == '8':
            wallet_name = input("Enter wallet name: ")
            view_address(wallet_name)
        elif choice == '9':
            print("Exiting...")
            break
        else:
            print("Invalid choice. Please try again.")

if __name__ == '__main__':
    main()
```

テストネットで送金できるか試そうと思ってアドレス生成してテストネットBTC送ってみたんですが、送った事実を忘れて秘密鍵を削除してしまい、X tsats失ってしまいました。bitcoinlibの機能を自分好みにわかりすいインターフェースで表現しただけなので、多分うまくいってるんでしょう。もう取り出せませんが。