def __init__(self, blockr_domain, txd, unconfirmfun, confirmfun): threading.Thread.__init__(self) self.daemon = True self.blockr_domain = blockr_domain self.unconfirmfun = unconfirmfun self.confirmfun = confirmfun self.tx_output_set = set([(sv['script'], sv['value']) for sv in txd['outs']]) self.output_addresses = [btc.script_to_address(scrval[0], common.get_p2pk_vbyte()) for scrval in self.tx_output_set] common.debug('txoutset=' + pprint.pformat(self.tx_output_set)) common.debug('outaddrs=' + ','.join(self.output_addresses))
def add_tx_notify(self, txd, unconfirmfun, confirmfun, notifyaddr): if not self.notifythread: self.notifythread = BitcoinCoreNotifyThread(self) self.notifythread.start() one_addr_imported = False for outs in txd['outs']: addr = btc.script_to_address(outs['script'], common.get_p2pk_vbyte()) if self.rpc('getaccount', [addr]) != '': one_addr_imported = True break if not one_addr_imported: self.rpc('importaddress', [notifyaddr, 'joinmarket-notify', False]) tx_output_set = set([(sv['script'], sv['value']) for sv in txd['outs']]) self.txnotify_fun.append((tx_output_set, unconfirmfun, confirmfun))
def run_simple_send(self, n, m): #start yield generator with wallet1 yigen_proc = local_command(['python','yield-generator.py', str(self.wallets[0]['seed'])],bg=True) #A significant delay is needed to wait for the yield generator to sync its wallet time.sleep(30) #run a single sendpayment call with wallet2 amt = n*100000000 #in satoshis dest_address = btc.privkey_to_address(os.urandom(32), common.get_p2pk_vbyte()) try: for i in range(m): sp_proc = local_command(['python','sendpayment.py','--yes','-N','1', self.wallets[1]['seed'],\ str(amt), dest_address]) except subprocess.CalledProcessError, e: if yigen_proc: yigen_proc.terminate() print e.returncode print e.message raise
def run_nparty_join(self): yigen_procs = [] for i in range(self.n): ygp = local_command(['python','yield-generator.py',\ str(self.wallets[i]['seed'])], bg=True) time.sleep(2) #give it a chance yigen_procs.append(ygp) #A significant delay is needed to wait for the yield generators to sync time.sleep(60) #run a single sendpayment call amt = 100000000 #in satoshis dest_address = btc.privkey_to_address(os.urandom(32), common.get_p2pk_vbyte()) try: sp_proc = local_command(['python','sendpayment.py','--yes','-N', str(self.n),\ self.wallets[self.n]['seed'], str(amt), dest_address]) except subprocess.CalledProcessError, e: for ygp in yigen_procs: ygp.kill() print e.returncode print e.message raise
def run_simple_send(self, n, m): #start yield generator with wallet1 yigen_proc = local_command( ['python', 'yield-generator.py', str(self.wallets[0]['seed'])], bg=True) #A significant delay is needed to wait for the yield generator to sync its wallet time.sleep(30) #run a single sendpayment call with wallet2 amt = n * 100000000 #in satoshis dest_address = btc.privkey_to_address(os.urandom(32), common.get_p2pk_vbyte()) try: for i in range(m): sp_proc = local_command(['python','sendpayment.py','--yes','-N','1', self.wallets[1]['seed'],\ str(amt), dest_address]) except subprocess.CalledProcessError, e: if yigen_proc: yigen_proc.terminate() print e.returncode print e.message raise
def main(): parser = OptionParser( usage= 'usage: %prog [options] [auth utxo] [cjamount] [cjaddr] [changeaddr] [utxos..]', description= 'Creates an unsigned coinjoin transaction. Outputs a partially signed transaction ' + 'hex string. The user must sign their inputs independently and broadcast them. The JoinMarket' + ' protocol requires the taker to have a single p2pk UTXO input to use to authenticate the ' + ' encrypted messages. For this reason you must pass auth utxo and the corresponding private key' ) #for cjamount=0 do a sweep, and ignore change address parser.add_option( '-f', '--txfee', action='store', type='int', dest='txfee', default=10000, help='miner fee contribution, in satoshis, default=10000') parser.add_option( '-w', '--wait-time', action='store', type='float', dest='waittime', help='wait time in seconds to allow orders to arrive, default=5', default=5) parser.add_option('-N', '--makercount', action='store', type='int', dest='makercount', help='how many makers to coinjoin with, default=2', default=2) parser.add_option( '-C', '--choose-cheapest', action='store_true', dest='choosecheapest', default=False, help='override weightened offers picking and choose cheapest') parser.add_option( '-P', '--pick-orders', action='store_true', dest='pickorders', default=False, help='manually pick which orders to take. doesn\'t work while sweeping.' ) parser.add_option('--yes', action='store_true', dest='answeryes', default=False, help='answer yes to everything') #TODO implement #parser.add_option('-n', '--no-network', action='store_true', dest='nonetwork', default=False, # help='dont query the blockchain interface, instead user must supply value of UTXOs on ' + # ' command line in the format txid:output/value-in-satoshi') (options, args) = parser.parse_args() if len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) auth_utxo = args[0] cjamount = int(args[1]) destaddr = args[2] changeaddr = args[3] cold_utxos = args[4:] common.load_program_config() addr_valid1, errormsg1 = validate_address(destaddr) #if amount = 0 dont bother checking changeaddr so user can write any junk if cjamount != 0: addr_valid2, errormsg2 = validate_address(changeaddr) else: addr_valid2 = True if not addr_valid1 or not addr_valid2: if not addr_valid1: print 'ERROR: Address invalid. ' + errormsg1 else: print 'ERROR: Address invalid. ' + errormsg2 return all_utxos = [auth_utxo] + cold_utxos query_result = common.bc_interface.query_utxo_set(all_utxos) if None in query_result: print query_result utxo_data = {} for utxo, data in zip(all_utxos, query_result): utxo_data[utxo] = {'address': data['address'], 'value': data['value']} auth_privkey = raw_input('input private key for ' + utxo_data[auth_utxo]['address'] + ' :') if utxo_data[auth_utxo]['address'] != btc.privtoaddr( auth_privkey, common.get_p2pk_vbyte()): print 'ERROR: privkey does not match auth utxo' return chooseOrdersFunc = None if options.pickorders and amount != 0: #cant use for sweeping chooseOrdersFunc = pick_order elif options.choosecheapest: chooseOrdersFunc = cheapest_order_choose else: #choose randomly (weighted) chooseOrdersFunc = weighted_order_choose common.nickname = random_nick() debug('starting sendpayment') class UnsignedTXWallet(common.AbstractWallet): def get_key_from_addr(self, addr): debug('getting privkey of ' + addr) if btc.privtoaddr(auth_privkey, common.get_p2pk_vbyte()) != addr: raise RuntimeError('privkey doesnt match given address') return auth_privkey wallet = UnsignedTXWallet() irc = IRCMessageChannel(common.nickname) taker = CreateUnsignedTx(irc, wallet, auth_utxo, cjamount, destaddr, changeaddr, utxo_data, options, chooseOrdersFunc) try: debug('starting irc') irc.run() except: debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'wallet_name', 'seed']) debug_dump_object(taker) import traceback debug(traceback.format_exc())
for m in range(wallet.max_mix_depth): printd('mixing depth %d m/0/%d/' % (m, m)) balance_depth = 0 for forchange in [0, 1]: printd(' ' + ('receive' if forchange==0 else 'change') + ' addresses m/0/%d/%d/' % (m, forchange)) for k in range(wallet.index[m][forchange] + options.gaplimit): addr = wallet.get_addr(m, forchange, k) balance = 0.0 for addrvalue in wallet.unspent.values(): if addr == addrvalue['address']: balance += addrvalue['value'] balance_depth += balance used = ('used' if k < wallet.index[m][forchange] else ' new') privkey = btc.encode_privkey(wallet.get_key(m, forchange, k), 'wif_compressed', get_p2pk_vbyte()) if options.showprivkey else '' if method == 'displayall' or balance > 0 or (used == ' new' and forchange==0): printd(' m/0/%d/%d/%03d %-35s%s %.8f btc %s' % (m, forchange, k, addr, used, balance/1e8, privkey)) if m in wallet.imported_privkeys: printd(' import addresses') for privkey in wallet.imported_privkeys[m]: addr = btc.privtoaddr(privkey, common.get_p2pk_vbyte()) balance = 0.0 for addrvalue in wallet.unspent.values(): if addr == addrvalue['address']: balance += addrvalue['value'] used = (' used' if balance > 0.0 else 'empty') balance_depth += balance wip_privkey = btc.encode_privkey(privkey, 'wif_compressed', get_addr_vbyte()) if options.showprivkey else '' printd(' '*13 + '%-35s%s %.8f btc %s' % (addr, used, balance/1e8, wip_privkey))
printd('mixing depth %d m/0/%d/' % (m, m)) balance_depth = 0 for forchange in [0, 1]: printd(' ' + ('receive' if forchange == 0 else 'change') + ' addresses m/0/%d/%d/' % (m, forchange)) for k in range(wallet.index[m][forchange] + options.gaplimit): addr = wallet.get_addr(m, forchange, k) balance = 0.0 for addrvalue in wallet.unspent.values(): if addr == addrvalue['address']: balance += addrvalue['value'] balance_depth += balance used = ('used' if k < wallet.index[m][forchange] else ' new') privkey = btc.encode_privkey( wallet.get_key(m, forchange, k), 'wif_compressed', get_p2pk_vbyte()) if options.showprivkey else '' if method == 'displayall' or balance > 0 or (used == ' new' and forchange == 0): printd( ' m/0/%d/%d/%03d %-35s%s %.8f btc %s' % (m, forchange, k, addr, used, balance / 1e8, privkey)) if m in wallet.imported_privkeys: printd(' import addresses') for privkey in wallet.imported_privkeys[m]: addr = btc.privtoaddr(privkey, common.get_p2pk_vbyte()) balance = 0.0 for addrvalue in wallet.unspent.values(): if addr == addrvalue['address']: balance += addrvalue['value'] used = (' used' if balance > 0.0 else 'empty') balance_depth += balance
print 'mixing depth %d m/0/%d/' % (m, m) balance_depth = 0 for forchange in [0, 1]: print(' ' + ('receive' if forchange == 0 else 'change') + ' addresses m/0/%d/%d/' % (m, forchange)) for k in range(wallet.index[m][forchange] + options.gaplimit): addr = wallet.get_addr(m, forchange, k) balance = 0.0 for addrvalue in wallet.unspent.values(): if addr == addrvalue['address']: balance += addrvalue['value'] balance_depth += balance used = ('used' if k < wallet.index[m][forchange] else ' new') privkey = btc.encode_privkey( wallet.get_key(m, forchange, k), 'wif_compressed', get_p2pk_vbyte()) if options.showprivkey else '' if method == 'displayall' or balance > 0 or (used == ' new' and forchange == 0): print ' m/0/%d/%d/%03d %-35s%s %.8f btc %s' % ( m, forchange, k, addr, used, balance / 1e8, privkey) print 'for mixdepth=%d balance=%.8fbtc' % (m, balance_depth / 1e8) total_balance += balance_depth print 'total balance = %.8fbtc' % (total_balance / 1e8) elif method == 'summary': total_balance = 0 for m in range(wallet.max_mix_depth): balance_depth = 0 for forchange in [0, 1]: for k in range(wallet.index[m][forchange]): addr = wallet.get_addr(m, forchange, k) for addrvalue in wallet.unspent.values():
def get_key_from_addr(self, addr): debug('getting privkey of ' + addr) if btc.privtoaddr(auth_privkey, common.get_p2pk_vbyte()) != addr: raise RuntimeError('privkey doesnt match given address') return auth_privkey
def main(): parser = OptionParser(usage='usage: %prog [options] [auth utxo] [cjamount] [cjaddr] [changeaddr] [utxos..]', description='Creates an unsigned coinjoin transaction. Outputs a partially signed transaction ' + 'hex string. The user must sign their inputs independently and broadcast them. The JoinMarket' + ' protocol requires the taker to have a single p2pk UTXO input to use to authenticate the ' + ' encrypted messages. For this reason you must pass auth utxo and the corresponding private key') #for cjamount=0 do a sweep, and ignore change address parser.add_option('-f', '--txfee', action='store', type='int', dest='txfee', default=10000, help='total miner fee in satoshis, default=10000') parser.add_option('-w', '--wait-time', action='store', type='float', dest='waittime', help='wait time in seconds to allow orders to arrive, default=5', default=5) parser.add_option('-N', '--makercount', action='store', type='int', dest='makercount', help='how many makers to coinjoin with, default=2', default=2) parser.add_option('-C','--choose-cheapest', action='store_true', dest='choosecheapest', default=False, help='override weightened offers picking and choose cheapest') parser.add_option('-P','--pick-orders', action='store_true', dest='pickorders', default=False, help='manually pick which orders to take. doesn\'t work while sweeping.') parser.add_option('--yes', action='store_true', dest='answeryes', default=False, help='answer yes to everything') #TODO implement #parser.add_option('-n', '--no-network', action='store_true', dest='nonetwork', default=False, # help='dont query the blockchain interface, instead user must supply value of UTXOs on ' + # ' command line in the format txid:output/value-in-satoshi') (options, args) = parser.parse_args() if len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) auth_utxo = args[0] cjamount = int(args[1]) destaddr = args[2] changeaddr = args[3] cold_utxos = args[4:] common.load_program_config() addr_valid1, errormsg1 = validate_address(destaddr) #if amount = 0 dont bother checking changeaddr so user can write any junk if cjamount != 0: addr_valid2, errormsg2 = validate_address(changeaddr) else: addr_valid2 = True if not addr_valid1 or not addr_valid2: if not addr_valid1: print 'ERROR: Address invalid. ' + errormsg1 else: print 'ERROR: Address invalid. ' + errormsg2 return all_utxos = [auth_utxo] + cold_utxos query_result = common.bc_interface.query_utxo_set(all_utxos) if None in query_result: print query_result utxo_data = {} for utxo, data in zip(all_utxos, query_result): utxo_data[utxo] = {'address': data['address'], 'value': data['value']} auth_privkey = raw_input('input private key for ' + utxo_data[auth_utxo]['address'] + ' :') if utxo_data[auth_utxo]['address'] != btc.privtoaddr(auth_privkey, common.get_p2pk_vbyte()): print 'ERROR: privkey does not match auth utxo' return chooseOrdersFunc = None if options.pickorders and amount != 0: #cant use for sweeping chooseOrdersFunc = pick_order elif options.choosecheapest: chooseOrdersFunc = cheapest_order_choose else: #choose randomly (weighted) chooseOrdersFunc = weighted_order_choose common.nickname = random_nick() debug('starting sendpayment') class UnsignedTXWallet(common.AbstractWallet): def get_key_from_addr(self, addr): debug('getting privkey of ' + addr) if btc.privtoaddr(auth_privkey, common.get_p2pk_vbyte()) != addr: raise RuntimeError('privkey doesnt match given address') return auth_privkey wallet = UnsignedTXWallet() irc = IRCMessageChannel(common.nickname) taker = CreateUnsignedTx(irc, wallet, auth_utxo, cjamount, destaddr, changeaddr, utxo_data, options, chooseOrdersFunc) try: debug('starting irc') irc.run() except: debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'wallet_name', 'seed']) debug_dump_object(taker) import traceback debug(traceback.format_exc())
def sync_addresses(self, wallet): if isinstance(wallet, common.BitcoinCoreWallet): return common.debug('requesting wallet history') wallet_name = self.get_wallet_name(wallet) addr_req_count = 20 wallet_addr_list = [] for mix_depth in range(wallet.max_mix_depth): for forchange in [0, 1]: wallet_addr_list += [ wallet.get_new_addr(mix_depth, forchange) for i in range(addr_req_count) ] wallet.index[mix_depth][forchange] = 0 #makes more sense to add these in an account called "joinmarket-imported" but its much # simpler to add to the same account here for privkey_list in wallet.imported_privkeys.values(): for privkey in privkey_list: imported_addr = btc.privtoaddr(privkey, common.get_p2pk_vbyte()) wallet_addr_list.append(imported_addr) imported_addr_list = self.rpc('getaddressesbyaccount', [wallet_name]) if not set(wallet_addr_list).issubset(set(imported_addr_list)): self.add_watchonly_addresses(wallet_addr_list, wallet_name) return buf = self.rpc('listtransactions', [wallet_name, 1000, 0, True]) txs = buf # If the buffer's full, check for more, until it ain't while len(buf) == 1000: buf = self.rpc( 'listtransactions', [wallet_name, 1000, len(txs), True]) txs += buf #TODO check whether used_addr_list can be a set, may be faster (if its a hashset) and allows # using issubset() here and setdiff() for finding which addresses need importing #TODO also check the fastest way to build up python lists, i suspect using += is slow used_addr_list = [ tx['address'] for tx in txs if tx['category'] == 'receive' ] too_few_addr_mix_change = [] for mix_depth in range(wallet.max_mix_depth): for forchange in [0, 1]: unused_addr_count = 0 last_used_addr = '' breakloop = False while not breakloop: if unused_addr_count >= wallet.gaplimit and\ is_index_ahead_of_cache(wallet, mix_depth, forchange): break mix_change_addrs = [ wallet.get_new_addr(mix_depth, forchange) for i in range(addr_req_count) ] for mc_addr in mix_change_addrs: if mc_addr not in imported_addr_list: too_few_addr_mix_change.append( (mix_depth, forchange)) breakloop = True break if mc_addr in used_addr_list: last_used_addr = mc_addr unused_addr_count = 0 else: unused_addr_count += 1 if last_used_addr == '': wallet.index[mix_depth][forchange] = 0 else: wallet.index[mix_depth][ forchange] = wallet.addr_cache[last_used_addr][2] + 1 wallet_addr_list = [] if len(too_few_addr_mix_change) > 0: common.debug('too few addresses in ' + str(too_few_addr_mix_change)) for mix_depth, forchange in too_few_addr_mix_change: wallet_addr_list += [ wallet.get_new_addr(mix_depth, forchange) for i in range(addr_req_count * 3) ] self.add_watchonly_addresses(wallet_addr_list, wallet_name) return self.wallet_synced = True
def sync_addresses(self, wallet): if isinstance(wallet, common.BitcoinCoreWallet): return common.debug('requesting wallet history') wallet_name = self.get_wallet_name(wallet) addr_req_count = 20 wallet_addr_list = [] for mix_depth in range(wallet.max_mix_depth): for forchange in [0, 1]: wallet_addr_list += [wallet.get_new_addr(mix_depth, forchange) for i in range(addr_req_count)] wallet.index[mix_depth][forchange] = 0 #makes more sense to add these in an account called "joinmarket-imported" but its much # simpler to add to the same account here for privkey_list in wallet.imported_privkeys.values(): for privkey in privkey_list: imported_addr = btc.privtoaddr(privkey, common.get_p2pk_vbyte()) wallet_addr_list.append(imported_addr) imported_addr_list = self.rpc('getaddressesbyaccount', [wallet_name]) if not set(wallet_addr_list).issubset(set(imported_addr_list)): self.add_watchonly_addresses(wallet_addr_list, wallet_name) return buf = self.rpc('listtransactions', [wallet_name, 1000, 0, True]) txs = buf # If the buffer's full, check for more, until it ain't while len(buf) == 1000: buf = self.rpc('listtransactions', [wallet_name, 1000, len(txs), True]) txs += buf #TODO check whether used_addr_list can be a set, may be faster (if its a hashset) and allows # using issubset() here and setdiff() for finding which addresses need importing #TODO also check the fastest way to build up python lists, i suspect using += is slow used_addr_list = [tx['address'] for tx in txs if tx['category'] == 'receive'] too_few_addr_mix_change = [] for mix_depth in range(wallet.max_mix_depth): for forchange in [0, 1]: unused_addr_count = 0 last_used_addr = '' breakloop = False while not breakloop: if unused_addr_count >= wallet.gaplimit and\ wallet.index[mix_depth][forchange] >= wallet.index_cache[mix_depth][forchange]: break mix_change_addrs = [wallet.get_new_addr(mix_depth, forchange) for i in range(addr_req_count)] for mc_addr in mix_change_addrs: if mc_addr not in imported_addr_list: too_few_addr_mix_change.append((mix_depth, forchange)) breakloop = True break if mc_addr in used_addr_list: last_used_addr = mc_addr unused_addr_count = 0 else: unused_addr_count += 1 if last_used_addr == '': wallet.index[mix_depth][forchange] = 0 else: wallet.index[mix_depth][forchange] = wallet.addr_cache[last_used_addr][2] + 1 wallet_addr_list = [] if len(too_few_addr_mix_change) > 0: common.debug('too few addresses in ' + str(too_few_addr_mix_change)) for mix_depth, forchange in too_few_addr_mix_change: wallet_addr_list += [wallet.get_new_addr(mix_depth, forchange) for i in range(addr_req_count*3)] self.add_watchonly_addresses(wallet_addr_list, wallet_name) return self.wallet_synced = True