def create_tx_and_offerlist(cj_addr, changeaddr, other_output_scripts, cj_script=None, cj_change_script=None, offertype='swreloffer'): assert len(other_output_scripts) % 2 == 0, "bug in test" other_participant_count = len(other_output_scripts) // 2 cj_value = 100000000 maker_total_value = cj_value * 3 if cj_script is None: cj_script = btc.address_to_script(cj_addr) if cj_change_script is None: changeaddr = btc.address_to_script(cj_change_addr) inputs = create_tx_inputs(3) outputs = create_tx_outputs( (cj_script, cj_value), (cj_change_script, maker_total_value - cj_value), # cjfee=0, txfee=0 *((script, cj_value + (i%2)*(50000000+i)) \ for i, script in enumerate(other_output_scripts)) ) maker_utxos = [inputs[0]] tx = btc.deserialize(btc.mktx(inputs, outputs)) offerlist = construct_tx_offerlist(cj_addr, changeaddr, maker_utxos, maker_total_value, cj_value, offertype) return tx, offerlist
def main(): parser = OptionParser( usage= 'usage: %prog [options] utxo destaddr1 destaddr2 ..', description="For creating multiple utxos from one (for commitments in JM)." "Provide a utxo in form txid:N that has some unspent coins;" "Specify a list of destination addresses and the coins will" "be split equally between them (after bitcoin fees)." "You'll be prompted to enter the private key for the utxo" "during the run; it must be in WIF compressed format." "After the transaction is completed, the utxo strings for" "the new outputs will be shown." "Note that these utxos will not be ready for use as external" "commitments in Joinmarket until 5 confirmations have passed." " BE CAREFUL about handling private keys!" " Don't do this in insecure environments." " Also note this ONLY works for standard (p2pkh) utxos." ) parser.add_option( '-v', '--validate-utxos', action='store_true', dest='validate', help='validate the utxos and pubkeys provided against the blockchain', default=False ) parser.add_option( '-o', '--validate-only', action='store_true', dest='vonly', help='only validate the provided utxos (file or command line), not add', default=False ) (options, args) = parser.parse_args() load_program_config() if len(args) < 2: quit(parser, 'Invalid syntax') u = args[0] priv = raw_input( 'input private key for ' + u + ', in WIF compressed format : ') u, priv = get_utxo_info(','.join([u, priv])) if not u: quit(parser, "Failed to parse utxo info: " + u) destaddrs = args[1:] for d in destaddrs: if not validate_address(d): quit(parser, "Address was not valid; wrong network?: " + d) txsigned = sign(u, priv, destaddrs) log.debug("Got signed transaction:\n" + txsigned) log.debug("Deserialized:") log.debug(pformat(btc.deserialize(txsigned))) if raw_input('Would you like to push to the network? (y/n):')[0] != 'y': log.info("You chose not to broadcast the transaction, quitting.") return jm_single().bc_interface.pushtx(txsigned)
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))
'tx#', 'timestamp', 'type', 'amount/btc', 'balance-change/btc', 'balance/btc', 'coinjoin-n', 'total-fees', 'utxo-count', '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])