Exemple #1
0
def get_address_generator(script_pre, script_post, vbyte):
    counter = 0
    while True:
        script = script_pre + struct.pack('=LQQ', 0, 0, counter) + script_post
        addr = btc.script_to_address(script, vbyte)
        yield addr, binascii.hexlify(script)
        counter += 1
Exemple #2
0
def wallet_fetch_history(wallet, options):
    # sort txes in a db because python can be really bad with large lists
    con = sqlite3.connect(":memory:")
    con.row_factory = sqlite3.Row
    tx_db = con.cursor()
    tx_db.execute("CREATE TABLE transactions(txid TEXT, "
            "blockhash TEXT, blocktime INTEGER);")
    jm_single().debug_silence[0] = True
    wallet_name = jm_single().bc_interface.get_wallet_name(wallet)
    for wn in [wallet_name, ""]:
        buf = range(1000)
        t = 0
        while len(buf) == 1000:
            buf = jm_single().bc_interface.rpc('listtransactions', [wn,
                1000, t, True])
            t += len(buf)
            tx_data = ((tx['txid'], tx['blockhash'], tx['blocktime']) for tx
                    in buf if 'txid' in tx and 'blockhash' in tx and 'blocktime'
                    in tx)
            tx_db.executemany('INSERT INTO transactions VALUES(?, ?, ?);',
                    tx_data)
            txes = tx_db.execute('SELECT DISTINCT txid, blockhash, blocktime '
                    'FROM transactions ORDER BY blocktime').fetchall()
            wallet_addr_cache = wallet.addr_cache
    wallet_addr_set = set(wallet_addr_cache.keys())

    def s():
        return ',' if options.csv else ' '
    def sat_to_str(sat):
        return '%.8f'%(sat/1e8)
    def sat_to_str_p(sat):
        return '%+.8f'%(sat/1e8)
    def skip_n1(v):
        return '% 2s'%(str(v)) if v != -1 else ' #'
    def skip_n1_btc(v):
        return sat_to_str(v) if v != -1 else '#' + ' '*10
    def print_row(index, time, tx_type, amount, delta, balance, cj_n,
                  miner_fees, utxo_count, mixdepth_src, mixdepth_dst, txid):
        data = [index, datetime.fromtimestamp(time).strftime("%Y-%m-%d %H:%M"),
                tx_type, sat_to_str(amount), sat_to_str_p(delta),
                sat_to_str(balance), skip_n1(cj_n), sat_to_str(miner_fees),
                '% 3d' % utxo_count, skip_n1(mixdepth_src), skip_n1(mixdepth_dst)]
        if options.verbosity % 2 == 0: data += [txid]
        print(s().join(map('"{}"'.format, data)))


    field_names = ['tx#', 'timestamp', 'type', 'amount/btc',
            'balance-change/btc', 'balance/btc', 'coinjoin-n', 'total-fees',
            'utxo-count', 'mixdepth-from', 'mixdepth-to']
    if options.verbosity % 2 == 0: field_names += ['txid']
    if options.csv:
        print('Bumping verbosity level to 4 due to --csv flag')
        options.verbosity = 1
    if options.verbosity > 0: print(s().join(field_names))
    if options.verbosity <= 2: cj_batch = [0]*8 + [[]]*2
    balance = 0
    utxo_count = 0
    deposits = []
    deposit_times = []
    for i, tx in enumerate(txes):
        rpctx = jm_single().bc_interface.rpc('gettransaction', [tx['txid']])
        txhex = str(rpctx['hex'])
        txd = btc.deserialize(txhex)
        output_addr_values = dict(((btc.script_to_address(sv['script'],
            get_p2sh_vbyte()), sv['value']) for sv in txd['outs']))
        our_output_addrs = wallet_addr_set.intersection(
                output_addr_values.keys())

        from collections import Counter
        value_freq_list = sorted(Counter(output_addr_values.values())
                .most_common(), key=lambda x: -x[1])
        non_cj_freq = 0 if len(value_freq_list)==1 else sum(zip(
            *value_freq_list[1:])[1])
        is_coinjoin = (value_freq_list[0][1] > 1 and value_freq_list[0][1] in
                [non_cj_freq, non_cj_freq+1])
        cj_amount = value_freq_list[0][0]
        cj_n = value_freq_list[0][1]

        rpc_inputs = []
        for ins in txd['ins']:
            try:
                wallet_tx = jm_single().bc_interface.rpc('gettransaction',
                        [ins['outpoint']['hash']])
            except JsonRpcError:
                continue
            input_dict = btc.deserialize(str(wallet_tx['hex']))['outs'][ins[
                'outpoint']['index']]
            rpc_inputs.append(input_dict)

        rpc_input_addrs = set((btc.script_to_address(ind['script'],
            get_p2sh_vbyte()) for ind in rpc_inputs))
        our_input_addrs = wallet_addr_set.intersection(rpc_input_addrs)
        our_input_values = [ind['value'] for ind in rpc_inputs if btc.
                script_to_address(ind['script'], get_p2sh_vbyte()) in
                our_input_addrs]
        our_input_value = sum(our_input_values)
        utxos_consumed = len(our_input_values)

        tx_type = None
        amount = 0
        delta_balance = 0
        fees = 0
        mixdepth_src = -1
        mixdepth_dst = -1
        #TODO this seems to assume all the input addresses are from the same
        # mixdepth, which might not be true
        if len(our_input_addrs) == 0 and len(our_output_addrs) > 0:
            #payment to us
            amount = sum([output_addr_values[a] for a in our_output_addrs])
            tx_type = 'deposit    '
            cj_n = -1
            delta_balance = amount
            mixdepth_dst = tuple(wallet_addr_cache[a][0] for a in
                    our_output_addrs)
            if len(mixdepth_dst) == 1:
                mixdepth_dst = mixdepth_dst[0]
        elif len(our_input_addrs) == 0 and len(our_output_addrs) == 0:
            continue            # skip those that don't belong to our wallet
        elif len(our_input_addrs) > 0 and len(our_output_addrs) == 0:
            # we swept coins elsewhere
            if is_coinjoin:
                tx_type = 'cj sweepout'
                amount = cj_amount
                fees = our_input_value - cj_amount
            else:
                tx_type = 'sweep out  '
                amount = sum([v for v in output_addr_values.values()])
                fees = our_input_value - amount
            delta_balance = -our_input_value
            mixdepth_src = wallet_addr_cache[list(our_input_addrs)[0]][0]
        elif len(our_input_addrs) > 0 and len(our_output_addrs) == 1:
            # payment to somewhere with our change address getting the remaining
            change_value = output_addr_values[list(our_output_addrs)[0]]
            if is_coinjoin:
                tx_type = 'cj withdraw'
                amount = cj_amount
            else:
                tx_type = 'withdraw'
                #TODO does tx_fee go here? not my_tx_fee only?
                amount = our_input_value - change_value
                cj_n = -1
            delta_balance = change_value - our_input_value
            fees = our_input_value - change_value - cj_amount
            mixdepth_src = wallet_addr_cache[list(our_input_addrs)[0]][0]
        elif len(our_input_addrs) > 0 and len(our_output_addrs) == 2:
            #payment to self
            out_value = sum([output_addr_values[a] for a in our_output_addrs])
            if not is_coinjoin:
                print('this is wrong TODO handle non-coinjoin internal')
            tx_type = 'cj internal'
            amount = cj_amount
            delta_balance = out_value - our_input_value
            mixdepth_src = wallet_addr_cache[list(our_input_addrs)[0]][0]
            cj_addr = list(set([a for a,v in output_addr_values.iteritems()
                if v == cj_amount]).intersection(our_output_addrs))[0]
            mixdepth_dst = wallet_addr_cache[cj_addr][0]
        else:
            tx_type = 'unknown type'
            print('our utxos: ' + str(len(our_input_addrs)) \
                  + ' in, ' + str(len(our_output_addrs)) + ' out')
        balance += delta_balance
        utxo_count += (len(our_output_addrs) - utxos_consumed)
        index = '% 4d'%(i)
        timestamp = datetime.fromtimestamp(rpctx['blocktime']
                ).strftime("%Y-%m-%d %H:%M")
        utxo_count_str = '% 3d' % (utxo_count)
        if options.verbosity > 0:
            if options.verbosity <= 2:
                n = cj_batch[0]
                if tx_type == 'cj internal':
                    cj_batch[0] += 1
                    cj_batch[1] += rpctx['blocktime']
                    cj_batch[2] += amount
                    cj_batch[3] += delta_balance
                    cj_batch[4] = balance
                    cj_batch[5] += cj_n
                    cj_batch[6] += fees
                    cj_batch[7] += utxo_count
                    cj_batch[8] += [mixdepth_src]
                    cj_batch[9] += [mixdepth_dst]
                elif tx_type != 'unknown type':
                    if n > 0:
                        # print the previously-accumulated batch
                        print_row('N='+"%2d"%n, cj_batch[1]/n, 'cj batch   ',
                                  cj_batch[2], cj_batch[3], cj_batch[4],
                                  cj_batch[5]/n, cj_batch[6], cj_batch[7]/n,
                                  min(cj_batch[8]), max(cj_batch[9]), '...')
                    cj_batch = [0]*8 + [[]]*2 # reset the batch collector
                    # print batch terminating row
                    print_row(index, rpctx['blocktime'], tx_type, amount,
                              delta_balance, balance, cj_n, fees, utxo_count,
                              mixdepth_src, mixdepth_dst, tx['txid'])
            elif options.verbosity >= 5 or \
                 (options.verbosity >= 3 and tx_type != 'unknown type'):
                print_row(index, rpctx['blocktime'], tx_type, amount,
                          delta_balance, balance, cj_n, fees, utxo_count,
                          mixdepth_src, mixdepth_dst, tx['txid'])

        if tx_type != 'cj internal':
            deposits.append(delta_balance)
            deposit_times.append(rpctx['blocktime'])

    # we could have a leftover batch!
    if options.verbosity <= 2:
        n = cj_batch[0]
        if n > 0:
            print_row('N='+"%2d"%n, cj_batch[1]/n, 'cj batch   ', cj_batch[2],
                      cj_batch[3], cj_batch[4], cj_batch[5]/n, cj_batch[6],
                      cj_batch[7]/n, min(cj_batch[8]), max(cj_batch[9]), '...')


    bestblockhash = jm_single().bc_interface.rpc('getbestblockhash', [])
    try:
        #works with pruning enabled, but only after v0.12
        now = jm_single().bc_interface.rpc('getblockheader', [bestblockhash]
                )['time']
    except JsonRpcError:
        now = jm_single().bc_interface.rpc('getblock', [bestblockhash])['time']
    print('     %s best block is %s' % (datetime.fromtimestamp(now)
        .strftime("%Y-%m-%d %H:%M"), bestblockhash))
    print('total profit = ' + str(float(balance - sum(deposits)) / float(100000000)) + ' BTC')
    try:
        # https://gist.github.com/chris-belcher/647da261ce718fc8ca10
        import numpy as np
        from scipy.optimize import brentq
        deposit_times = np.array(deposit_times)
        now -= deposit_times[0]
        deposit_times -= deposit_times[0]
        deposits = np.array(deposits)
        def f(r, deposits, deposit_times, now, final_balance):
            return np.sum(np.exp((now - deposit_times) / 60.0 / 60 / 24 /
                365)**r * deposits) - final_balance
        r = brentq(f, a=1, b=-1, args=(deposits, deposit_times, now, balance))
        print('continuously compounded equivalent annual interest rate = ' +
              str(r * 100) + ' %')
        print('(as if yield generator was a bank account)')
    except ImportError:
        print('numpy/scipy not installed, unable to calculate effective ' +
                'interest rate')

    total_wallet_balance = sum(wallet.get_balance_by_mixdepth().values())
    if balance != total_wallet_balance:
        print(('BUG ERROR: wallet balance (%s) does not match balance from ' +
            'history (%s)') % (sat_to_str(total_wallet_balance),
                sat_to_str(balance)))
    if utxo_count != len(wallet.unspent):
        print(('BUG ERROR: wallet utxo count (%d) does not match utxo count from ' +
            'history (%s)') % (len(wallet.unspent), utxo_count))
Exemple #3
0
        'mixdepth-from', 'mixdepth-to'
    ]
    if options.csv:
        field_names += ['txid']
    l = s().join(field_names)
    print(l)
    balance = 0
    utxo_count = 0
    deposits = []
    deposit_times = []
    for i, tx in enumerate(txes):
        rpctx = jm_single().bc_interface.rpc('gettransaction', [tx['txid']])
        txhex = str(rpctx['hex'])
        txd = btc.deserialize(txhex)
        output_addr_values = dict(
            ((btc.script_to_address(sv['script'],
                                    get_p2pk_vbyte()), sv['value'])
             for sv in txd['outs']))
        our_output_addrs = wallet_addr_set.intersection(
            output_addr_values.keys())

        from collections import Counter
        value_freq_list = sorted(Counter(
            output_addr_values.values()).most_common(),
                                 key=lambda x: -x[1])
        non_cj_freq = 0 if len(value_freq_list) == 1 else sum(
            zip(*value_freq_list[1:])[1])
        is_coinjoin = (value_freq_list[0][1] > 1 and value_freq_list[0][1]
                       in [non_cj_freq, non_cj_freq + 1])
        cj_amount = value_freq_list[0][0]
        cj_n = value_freq_list[0][1]