"""
Bitcoin spray: distribute BTC from a coinbase.com account into many addresses
generated by a deterministic wallet (an electrum wallet).

1.0 BTC ->
    0.01 BTC
    0.01 BTC
    0.01 BTC
    etc..

This is particularly useful because coinbase.com is paying all transaction
fees.

"Coinbase pays fees on transactions of 0.001 BTC or greater."
https://coinbase.com/api/doc/1.0/transactions/send_money.html

I suggest disabling "send BTC" email notifications on coinbase.com for the
duration of this script so that the email doesn't get intercepted.

Make API calls to coinbase.com every time a new block is seen on the Bitcoin
network. These API calls will tell coinbase.com to send bitcoin from
coinbase.com to particular addresses in the hierarchical wallet.

Components:

[x] hierarchical wallet address generator thing
[x] resume
[x] listen/poll for new blocks
[x] coinbase.com api client (for sending bitcoins)

Limitations:

* doesn't consider reorgs at all
"""

COINBASE_API_KEY = "YOUR-API-KEY"
COINBASE_API_SECRET = "YOUR-API-SECRET"
from coinbase_passwords import *

import websocket
import json
import logging
import thread
import time
import datetime

# for coinbase stuff
import urllib2
import hashlib
import hmac

# The index number of the next address to use in the deterministic wallet. So,
# if the program just crashed for whatever reason, you would look at the last
# index used to send some BTC, and write that number plus one here.
address_indexer = 38

# Amount BTC to distribute per address. A better system would use a random
# number within some range.
per_address = 0.01

# Number of transactions per block to create. There is no guarantee that each
# transaction will be included in each block. Some might appear in future
# blocks, so the transaction count might be anywhere between 0 and the the
# total number of transactions that have been attempted but not yet included in
# a block. This number controls the creation of new transactions per block. A
# better system might monitor for unconfirmed transactions, and only create new
# transactions once the previous transactions have been confirmed at least
# once so that the total balance doesn't end up tied up in lousy unconfirmed
# transactions. A better system would use a random number within some range
# (including zero in that range).
transactions_per_block = 4
# Also, there is a minor incentive to keep the number of transactions per block
# low. In particular, and especially if you choose a high-entropy number for
# the value of "per_address", it will be easy for others to guess that the
# other addresses belong to you because the same account balances are being
# transferred. Similarly, a large increase in the number of small-value
# transactions in the blockchain in a given block is another piece of
# information that can be used to correlate the addresses as probably belonging
# to the same owner. For these reasons and others, splitting up the
# transactions into separate blocks helps to obfuscate your presence at least a
# little.

max_transactions = 1000

# sudo apt-get install electrum
# https://bitcointalk.org/index.php?topic=612143.0
import electrum

# load default electrum configuration
config = electrum.SimpleConfig()
storage = electrum.wallet.WalletStorage(config)
wallet = electrum.wallet.Wallet(storage)

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(message)s')
log = logging.getLogger(__name__)

def get_total_balance(wallet=wallet):
    """
    :return: total balance in satoshis (confirmed)
    """
    #return wallet.get_account_balance(0)[0]
    return wallet.get_balance()[0]

def get_address_balance(address, wallet=wallet):
    """
    :param address: some address
    :return: confirmed address balance in satoshis
    """
    return wallet.get_addr_balance(address)[0]

def get_nth_address(number, wallet=wallet):
    """
    Generate the nth address. Doesn't generate change addresses.

    :param number: the numberth public address to generate
    :return: address
    """
    return wallet.accounts[0].get_address(0, number)

def get_first_address_with_zero_balance(wallet=wallet, minimum=0, limit=10000):
    """
    Find an address that has a balance of zero. Ideally this would find an
    address that hasn't been used before, because it is possible that it will
    find an address that has been previously used and emptied back to zero.

    A better system would check the blockchain and find the first unused
    address, and then rename this function appropriately.

    :param limit: Number of search cycles to employ after exhausting
    pre-generated list of addresses.
    :param minimum: first usable address (skip everything before) (useful for
    resuming)
    :return: (number, address)
    """
    for (number, address) in enumerate(wallet.addresses()):
        balance = get_address_balance(address, wallet=wallet)
        if balance == 0 and number >= minimum:
            return (number, address)
    else:
        # Exhausted pre-generated addresses. Search for next address that has
        # zero balance.
        counter = number

        while counter <= limit:
            address = get_nth_address(counter, wallet=wallet)
            balance = get_address_balance(address, wallet=wallet)
            if balance == 0 and counter >= minimum:
                return (counter, address)
            counter += 1

        # Really I shouldn't use a limit, but I'm skeptical that 10,000
        # addresses are really in use. Human intervention required..
        raise Exception("Couldn't find an address with an empty balance.")

def execute_coinbase_http(url, body=None):
    """
    https://coinbase.com/docs/api/authentication
    """

    # just a precaution..
    if "https" not in url:
        raise Exception("i don't think so, tim")

    opener = urllib2.build_opener()
    nonce = int(time.time() * 1e6)
    message = str(nonce) + url + ('' if body is None else body)
    signature = hmac.new(COINBASE_API_SECRET, message, hashlib.sha256).hexdigest()
    opener.addheaders = [('ACCESS_KEY', COINBASE_API_KEY),
                         ('ACCESS_SIGNATURE', signature),
                         ('ACCESS_NONCE', nonce)]
    try:
        return opener.open(urllib2.Request(url, body, {'Content-Type': 'application/json'}))
    except urllib2.HTTPError as e:
        print e
        return e

def send_btc(amount, address):
    """
    Use coinbase.com to send some BTC to an address. The amount is in units of
    BTC. When the transaction is successfully created, coinbase.com will return
    some json with the "success" key set to json true.
    """
    # Don't debug with httpbin while the headers are enabled in
    # execute_coinbase_http.
    #url = "http://httpbin.org/post"

    url = "https://coinbase.com/api/v1/transactions/send_money"

    body = json.dumps({
        "transaction": {
            "to": address,
            "amount": amount,
        },
    })

    response = execute_coinbase_http(url, body=body)
    content = json.loads(response.read())
    return content

class BlockchainInfoWebSocketAPI(object):
    """
    http://blockchain.info/api/api_websocket
    """

    url = "ws://ws.blockchain.info/inv"

    @staticmethod
    def on_open(ws):
        """
        Spawn a function that pings blockchain.info every 30 seconds so that
        the websocket connection doesn't get killed from that end.
        """
        def run(*args):
            # subscribe to blocks
            BlockchainInfoWebSocketAPI.subscribe_to_blocks(ws)

            # ping every 25 seconds to prevent remote server from disconnecting
            while 1:
                log.debug("BlockchainInfoWebSocketAPI: doing heartbeat ping to blockchain.info")
                ws.send("")
                time.sleep(25)

        # run the "run" method in a new thread
        thread.start_new_thread(run, ())

    @staticmethod
    def on_close(ws):
        log.info("BlockchainInfoWebSocketAPI: closing websocket connection")

    @staticmethod
    def on_error(ws, error):
        log.exception("BlockchainInfoWebSocketAPI error: " + error)

    @staticmethod
    def on_message(ws, message):
        global transactions_per_block
        global per_address
        global address_indexer

        data = json.loads(message)

        if data["op"] == "block":
            log.info("BlockchainInfoWebSocketAPI: received new block")

            i = 0
            while i < transactions_per_block:
                amount = per_address
                (latest_index, address) = get_first_address_with_zero_balance(minimum=address_indexer)

                log.info("BlockchainInfoWebSocketAPI: sending {amount} BTC to address #{num} - {address}".format(
                    amount=amount,
                    num=latest_index,
                    address=address,
                ))

                response = send_btc(str(amount), address)

                log.info("BlockchainInfoWebSocketAPI: coinbase.com request successful? " + str(response["success"]))

                log.info(response)

                # Kinda lying, it's really just an indexer, so point it to the
                # next one please.
                address_indexer = latest_index + 1

                i += 1

    @staticmethod
    def subscribe_to_blocks(ws):
        """
        Communicates with blockchain.info to subscribe to block notifications.
        Use blocks_sub for blocks. The ping_block operation is used only for
        debugging (it immediately pings the last known block).
        """
        ws.send('{"op":"blocks_sub"}')
        # ws.send('{"op":"ping_block"}')

    @staticmethod
    def _run_forever():
        log.info("BlockchainInfoWebSocketAPI: begin blockchain.info websocket connection")
        ws = websocket.WebSocketApp(
            BlockchainInfoWebSocketAPI.url,
            on_message=BlockchainInfoWebSocketAPI.on_message,
            on_error=BlockchainInfoWebSocketAPI.on_error,
            on_close=BlockchainInfoWebSocketAPI.on_close,
            on_open=BlockchainInfoWebSocketAPI.on_open,
        )
        ws.run_forever()
        return ws

    @staticmethod
    def run_forever():
        delay = 1
        while 1:
            # try to use run_forever, wait between with an exponential backoff
            try:
                BlockchainInfoWebSocketAPI._run_forever()
            except websocket.WebSocketException, exc:
                log.exception(exc)
                log.warning("BlockchainInfoWebSocketAPI: will attempt to restart connection in {0} seconds...".format(delay))
                time.sleep(delay)
                delay *= 2
            except Exception, exc:
                raise exc

def quick_time_estimate(per_address, transactions_per_block, max_transactions):
    """
    Estimate the total BTC to distribute, how many blocks to use, how many
    hours this will probably take (assuming 10 minutes per block), and an
    estimated timestamp for when everything will be done.
    """
    total_btc = per_address * max_transactions

    # Number of required blocks is based on the total number of transactions.
    blocks = float(max_transactions) / float(transactions_per_block)

    # each block takes 10 minutes (uh, on average, or rather, that's the target)
    minutes = blocks * 10

    # each hour takes 60 minutes
    hours = minutes / 60

    timefuture = datetime.datetime.now() + datetime.timedelta(minutes=minutes)

    return (total_btc, blocks, hours, timefuture)

def dump_estimates(
    per_address=per_address,
    transactions_per_block=transactions_per_block,
    max_transactions=max_transactions,
):
    (total_btc, blocks, hours, timefuture) = quick_time_estimate(per_address, transactions_per_block, max_transactions)
    output = "total_btc: {total_btc}\n"
    output += "blocks: {blocks}\n"
    output += "hours: {hours}\n"
    output += "approximately done at: {timefuture}"

    output = output.format(
        total_btc=total_btc,
        blocks=blocks,
        hours=hours,
        timefuture=timefuture,
    )

    return output

if __name__ == "__main__":
    print dump_estimates(), "\n"
    BlockchainInfoWebSocketAPI.run_forever()