def make_wallets(n, wallet_structures=None, mean_amt=1, sdev_amt=0, start_index=0): '''n: number of wallets to be created wallet_structure: array of n arrays , each subarray specifying the number of addresses to be populated with coins at each depth (for now, this will only populate coins into 'receive' addresses) mean_amt: the number of coins (in btc units) in each address as above sdev_amt: if randomness in amouts is desired, specify here. Returns: a dict of dicts of form {0:{'seed':seed,'wallet':Wallet object},1:..,}''' if len(wallet_structures) != n: raise Exception("Number of wallets doesn't match wallet structures") seeds = chunks(binascii.hexlify(os.urandom(15 * n)), n) wallets = {} for i in range(n): wallets[i + start_index] = { 'seed': seeds[i], 'wallet': Wallet(seeds[i], max_mix_depth=5) } for j in range(5): for k in range(wallet_structures[i][j]): deviation = sdev_amt * random.random() amt = mean_amt - sdev_amt / 2.0 + deviation if amt < 0: amt = 0.001 jm_single().bc_interface.grab_coins( wallets[i + start_index]['wallet'].get_external_addr(j), amt) return wallets
def test_blockr_sync(setup_blockr, net, seed, gaplimit, showprivkey, method): jm_single().config.set("BLOCKCHAIN", "network", net) wallet = Wallet(seed, max_mix_depth=5) jm_single().bc_interface.sync_wallet(wallet) #copy pasted from wallet-tool; some boiled down form of #this should really be in wallet.py in the joinmarket module. def cus_print(s): print s total_balance = 0 for m in range(wallet.max_mix_depth): cus_print('mixing depth %d m/0/%d/' % (m, m)) balance_depth = 0 for forchange in [0, 1]: cus_print(' ' + ('external' if forchange == 0 else 'internal') + ' addresses m/0/%d/%d/' % (m, forchange)) for k in range(wallet.index[m][forchange] + 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') if showprivkey: if btc.secp_present: privkey = btc.wif_compressed_privkey( wallet.get_key(m, forchange, k), get_p2pk_vbyte()) else: privkey = btc.encode_privkey( wallet.get_key(m, forchange, k), 'wif_compressed', get_p2pk_vbyte()) else: privkey = '' if (method == 'displayall' or balance > 0 or (used == ' new' and forchange == 0)): cus_print( ' m/0/%d/%d/%03d %-35s%s %.8f btc %s' % (m, forchange, k, addr, used, balance / 1e8, privkey)) total_balance += balance_depth print('for mixdepth=%d balance=%.8fbtc' % (m, balance_depth / 1e8)) assert total_balance == 96143257
def test_blockr_sync(setup_blockr, net, seed, gaplimit, showprivkey, method): jm_single().config.set("BLOCKCHAIN", "network", net) wallet = Wallet(seed, max_mix_depth = 5) jm_single().bc_interface.sync_wallet(wallet) #copy pasted from wallet-tool; some boiled down form of #this should really be in wallet.py in the joinmarket module. def cus_print(s): print s total_balance = 0 for m in range(wallet.max_mix_depth): cus_print('mixing depth %d m/0/%d/' % (m, m)) balance_depth = 0 for forchange in [0, 1]: cus_print(' ' + ('external' if forchange == 0 else 'internal') + ' addresses m/0/%d/%d/' % (m, forchange)) for k in range(wallet.index[m][forchange] + 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') if showprivkey: if btc.secp_present: privkey = btc.wif_compressed_privkey( wallet.get_key(m, forchange, k), get_p2pk_vbyte()) else: privkey = btc.encode_privkey(wallet.get_key(m, forchange, k), 'wif_compressed', get_p2pk_vbyte()) else: privkey = '' if (method == 'displayall' or balance > 0 or (used == ' new' and forchange == 0)): cus_print(' m/0/%d/%d/%03d %-35s%s %.8f btc %s' % (m, forchange, k, addr, used, balance / 1e8, privkey)) total_balance += balance_depth print('for mixdepth=%d balance=%.8fbtc' % (m, balance_depth / 1e8)) assert total_balance == 96143257
def main(): load_program_config() if isinstance(jm_single().bc_interface, blockchaininterface.BlockrInterface): print('You are using the blockr.io website') print('You should setup JoinMarket with Bitcoin Core.') ret = raw_input('\nContinue Anyways? (y/n):') if ret[0] != 'y': return wallet = Wallet(wallet_file, max_mix_depth=mix_levels) jm_single().bc_interface.sync_wallet(wallet) jm_single().nickname = nickname log.debug('starting yield generator') irc = IRCMessageChannel( jm_single().nickname, realname='btcint=' + jm_single().config.get("BLOCKCHAIN", "blockchain_source"), password=nickserv_password) maker = YieldGenerator(irc, wallet) def timer_loop(startup=False): # for oscillator if not startup: maker.refresh_offers() poss_refresh = [] for x in offer_levels: recent_transactions = get_recent_transactions(x['time_frame']) if recent_transactions: oldest_transaction_time = recent_transactions[-1]['timestamp'] else: oldest_transaction_time = datetime.datetime.now() next_refresh = oldest_transaction_time + datetime.timedelta( hours=x['time_frame'], seconds=1) poss_refresh.append(next_refresh) next_refresh = sorted(poss_refresh, key=lambda x: x)[0] td = next_refresh - datetime.datetime.now() seconds_till = (td.days * 24 * 60 * 60) + td.seconds log.debug('Next offer refresh for ' + nickname + ' at ' + next_refresh.strftime("%Y-%m-%d %I:%M:%S %p")) log.debug('...or after a new transaction shows up.') t = threading.Timer(seconds_till, timer_loop) t.daemon = True t.start() timer_loop(startup=True) try: log.debug('connecting to irc') irc.run() except: log.debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) debug_dump_object(maker) debug_dump_object(irc) import traceback log.debug(traceback.format_exc())
def test_blockr_sync(setup_blockr, net, seed, gaplimit, showprivkey, method): jm_single().config.set("BLOCKCHAIN", "network", net) wallet = Wallet(seed, max_mix_depth=5) jm_single().bc_interface.sync_wallet(wallet) # copy pasted from wallet-tool; some boiled down form of # this should really be in wallet.py in the joinmarket module. def cus_print(s): print s total_balance = 0 for m in range(wallet.max_mix_depth): cus_print("mixing depth %d m/0/%d/" % (m, m)) balance_depth = 0 for forchange in [0, 1]: cus_print(" " + ("external" if forchange == 0 else "internal") + " addresses m/0/%d/%d/" % (m, forchange)) for k in range(wallet.index[m][forchange] + 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" if showprivkey: privkey = btc.wif_compressed_privkey(wallet.get_key(m, forchange, k), get_p2pk_vbyte()) else: privkey = "" if method == "displayall" or balance > 0 or (used == " new" and forchange == 0): cus_print( " m/0/%d/%d/%03d %-35s%s %.8f btc %s" % (m, forchange, k, addr, used, balance / 1e8, privkey) ) total_balance += balance_depth print ("for mixdepth=%d balance=%.8fbtc" % (m, balance_depth / 1e8)) assert total_balance == 96085297
def main(): load_program_config() import sys seed = sys.argv[1] if isinstance(jm_single().bc_interface, blockchaininterface.BlockrInterface): print( '\nYou are running a yield generator by polling the blockr.io website' ) print( 'This is quite bad for privacy. That site is owned by coinbase.com' ) print( 'Also your bot will run faster and more efficently, you can be immediately notified of new bitcoin network' ) print( ' information so your money will be working for you as hard as possible' ) print( 'Learn how to setup JoinMarket with Bitcoin Core: https://github.com/chris-belcher/joinmarket/wiki/Running-JoinMarket-with-Bitcoin-Core-full-node' ) ret = raw_input('\nContinue? (y/n):') if ret[0] != 'y': return wallet = Wallet(seed, max_mix_depth=mix_levels) jm_single().bc_interface.sync_wallet(wallet) jm_single().nickname = nickname log.debug('starting yield generator') irc = IRCMessageChannel( jm_single().nickname, realname='btcint=' + jm_single().config.get("BLOCKCHAIN", "blockchain_source"), password=nickserv_password) maker = YieldGenerator(irc, wallet) try: log.debug('connecting to irc') irc.run() except: log.debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) debug_dump_object(maker) debug_dump_object(irc) import traceback log.debug(traceback.format_exc())
def make_wallets(n, wallet_structures=None, mean_amt=1, sdev_amt=0, start_index=0, fixed_seeds=None, test_wallet=False, passwords=None): '''n: number of wallets to be created wallet_structure: array of n arrays , each subarray specifying the number of addresses to be populated with coins at each depth (for now, this will only populate coins into 'receive' addresses) mean_amt: the number of coins (in btc units) in each address as above sdev_amt: if randomness in amouts is desired, specify here. Returns: a dict of dicts of form {0:{'seed':seed,'wallet':Wallet object},1:..,} Default Wallet constructor is joinmarket.Wallet, else use TestWallet, which takes a password parameter as in the list passwords. ''' if len(wallet_structures) != n: raise Exception("Number of wallets doesn't match wallet structures") if not fixed_seeds: seeds = chunks(binascii.hexlify(os.urandom(15 * n)), 15 * 2) else: seeds = fixed_seeds wallets = {} for i in range(n): if test_wallet: w = TestWallet(seeds[i], max_mix_depth=5, pwd=passwords[i]) else: w = Wallet(seeds[i], max_mix_depth=5) wallets[i + start_index] = {'seed': seeds[i], 'wallet': w} for j in range(5): for k in range(wallet_structures[i][j]): deviation = sdev_amt * random.random() amt = mean_amt - sdev_amt / 2.0 + deviation if amt < 0: amt = 0.001 amt = float(Decimal(amt).quantize(Decimal(10)**-8)) jm_single().bc_interface.grab_coins( wallets[i + start_index]['wallet'].get_external_addr(j), amt) #reset the index so the coins can be seen if running in same script wallets[ i + start_index]['wallet'].index[j][0] -= wallet_structures[i][j] return wallets
def setUp(self): #create 7 new random wallets. #put about 10 coins in each, spread over random mixdepths #in units of 0.5 seeds = chunks(binascii.hexlify(os.urandom(15 * 7)), 7) self.wallets = {} for i in range(7): self.wallets[i] = {'seed': seeds[i], 'wallet': Wallet(seeds[i], max_mix_depth=5)} #adding coins somewhat randomly, spread over all 5 depths for i in range(7): w = self.wallets[i]['wallet'] for j in range(5): for k in range(4): base = 0.001 if i == 6 else 2.0 amt = base + random.random( ) #average is 0.5 for tumbler, else 1.5 jm_single().bc_interface.grab_coins( w.get_external_addr(j), amt)
def main(): parser = OptionParser( usage= 'usage: %prog [options] [wallet file / fromaccount] [amount] [destaddr]', description='Sends a single payment from a given mixing depth of your ' + 'wallet to an given address using coinjoin and then switches off. Also sends from bitcoinqt. ' + 'Setting amount to zero will do a sweep, where the entire mix depth is emptied' ) parser.add_option( '-f', '--txfee', action='store', type='int', dest='txfee', default=5000, help= 'number of satoshis per participant to use as the initial estimate ' + 'for the total transaction fee, default=5000, note that this is adjusted ' + 'based on the estimated fee calculated after tx construction, based on ' + 'policy set in joinmarket.cfg.') 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('-m', '--mixdepth', action='store', type='int', dest='mixdepth', help='mixing depth to spend from, default=0', default=0) parser.add_option('-g', '--gap-limit', type="int", action='store', dest='gaplimit', help='gap limit for wallet, default=6', default=6) parser.add_option('--yes', action='store_true', dest='answeryes', default=False, help='answer yes to everything') parser.add_option( '--rpcwallet', action='store_true', dest='userpcwallet', default=False, help=('Use the Bitcoin Core wallet through json rpc, instead ' 'of the internal joinmarket wallet. Requires ' 'blockchain_source=json-rpc')) (options, args) = parser.parse_args() if len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) wallet_name = args[0] amount = int(args[1]) destaddr = args[2] load_program_config() addr_valid, errormsg = validate_address(destaddr) if not addr_valid: print('ERROR: Address invalid. ' + errormsg) return chooseOrdersFunc = None if options.pickorders: chooseOrdersFunc = pick_order if amount == 0: print 'WARNING: You may have to pick offers multiple times' print 'WARNING: due to manual offer picking while sweeping' elif options.choosecheapest: chooseOrdersFunc = cheapest_order_choose else: # choose randomly (weighted) chooseOrdersFunc = weighted_order_choose jm_single().nickname = random_nick() log.debug('starting sendpayment') if not options.userpcwallet: wallet = Wallet(wallet_name, options.mixdepth + 1, options.gaplimit) else: wallet = BitcoinCoreWallet(fromaccount=wallet_name) jm_single().bc_interface.sync_wallet(wallet) irc = IRCMessageChannel(jm_single().nickname) taker = SendPayment(irc, wallet, destaddr, amount, options.makercount, options.txfee, options.waittime, options.mixdepth, options.answeryes, chooseOrdersFunc) try: log.debug('starting irc') irc.run() except: log.debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'wallet_name', 'seed']) debug_dump_object(taker) import traceback log.debug(traceback.format_exc())
noscan_methods = ['showseed', 'importprivkey'] if len(args) < 1: parser.error('Needs a wallet file or method') sys.exit(0) load_program_config() if args[0] in noseed_methods: method = args[0] else: seed = args[0] method = ('display' if len(args) == 1 else args[1].lower()) wallet = Wallet(seed, options.maxmixdepth, options.gaplimit, extend_mixdepth=not maxmixdepth_configured, storepassword=(method == 'importprivkey')) if method not in noscan_methods: # if nothing was configured, we override bitcoind's options so that # unconfirmed balance is included in the wallet display by default if 'listunspent_args' not in jm_single().config.options('POLICY'): jm_single().config.set('POLICY','listunspent_args', '[0]') jm_single().bc_interface.sync_wallet(wallet) if method == 'display' or method == 'displayall' or method == 'summary': def cus_print(s): if method != 'summary': print(s)
def ygmain(ygclass, txfee=1000, cjfee_a=200, cjfee_r=0.002, ordertype='reloffer', nickserv_password='', minsize=100000, mix_levels=5, gaplimit=6): import sys parser = OptionParser(usage='usage: %prog [options] [wallet file]') parser.add_option('-o', '--ordertype', action='store', type='string', dest='ordertype', default=ordertype, help='type of order; can be either reloffer or absoffer') parser.add_option('-t', '--txfee', action='store', type='int', dest='txfee', default=txfee, help='minimum miner fee in satoshis') parser.add_option('-c', '--cjfee', action='store', type='string', dest='cjfee', default='', help='requested coinjoin fee in satoshis or proportion') parser.add_option('-p', '--password', action='store', type='string', dest='password', default=nickserv_password, help='irc nickserv password') parser.add_option('-s', '--minsize', action='store', type='int', dest='minsize', default=minsize, help='minimum coinjoin size in satoshis') parser.add_option('-m', '--mixlevels', action='store', type='int', dest='mixlevels', default=mix_levels, help='number of mixdepths to use') parser.add_option('-g', '--gap-limit', action='store', type="int", dest='gaplimit', default=6, help='gap limit for wallet, default=6') parser.add_option('--fast', action='store_true', dest='fastsync', default=False, help=('choose to do fast wallet sync, only for Core and ' 'only for previously synced wallet')) (options, args) = parser.parse_args() if len(args) < 1: parser.error('Needs a wallet') sys.exit(0) seed = args[0] ordertype = options.ordertype txfee = options.txfee if ordertype == 'reloffer': if options.cjfee != '': cjfee_r = options.cjfee # minimum size is such that you always net profit at least 20% #of the miner fee minsize = max(int(1.2 * txfee / float(cjfee_r)), options.minsize) elif ordertype == 'absoffer': if options.cjfee != '': cjfee_a = int(options.cjfee) minsize = options.minsize else: parser.error('You specified an incorrect order type which ' +\ 'can be either reloffer or absoffer') sys.exit(0) nickserv_password = options.password mix_levels = options.mixlevels load_program_config() if isinstance(jm_single().bc_interface, BlockrInterface): c = ('\nYou are running a yield generator by polling the blockr.io ' 'website. This is quite bad for privacy. That site is owned by ' 'coinbase.com Also your bot will run faster and more efficently, ' 'you can be immediately notified of new bitcoin network ' 'information so your money will be working for you as hard as ' 'possibleLearn how to setup JoinMarket with Bitcoin Core: ' 'https://github.com/chris-belcher/joinmarket/wiki/Running' '-JoinMarket-with-Bitcoin-Core-full-node') print(c) ret = raw_input('\nContinue? (y/n):') if ret[0] != 'y': return wallet = Wallet(seed, max_mix_depth=mix_levels, gaplimit=gaplimit) sync_wallet(wallet, fast=options.fastsync) mcs = [ IRCMessageChannel( c, realname='btcint=' + jm_single().config.get("BLOCKCHAIN", "blockchain_source"), password=nickserv_password) for c in get_irc_mchannels() ] mcc = MessageChannelCollection(mcs) log.info('starting yield generator') maker = ygclass(mcc, wallet, [ options.txfee, cjfee_a, cjfee_r, options.ordertype, options.minsize, mix_levels ]) try: log.info('connecting to message channels') mcc.run() except: log.warn('Quitting! Dumping object contents to logfile.') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) debug_dump_object(maker) debug_dump_object(mcc, ['nick_priv', 'nick_pkh_raw']) import traceback log.debug(traceback.format_exc())
] if len(args) < 1: parser.error('Needs a wallet file or method') sys.exit(0) load_program_config() if args[0] in noseed_methods: method = args[0] else: seed = args[0] method = ('display' if len(args) == 1 else args[1].lower()) wallet = Wallet(seed, options.maxmixdepth, options.gaplimit, extend_mixdepth=not maxmixdepth_configured, storepassword=(method == 'importprivkey')) if method == 'history' and not isinstance(jm_single().bc_interface, BitcoinCoreInterface): print('showing history only available when using the Bitcoin Core ' + 'blockchain interface') sys.exit(0) if method not in noscan_methods: # if nothing was configured, we override bitcoind's options so that # unconfirmed balance is included in the wallet display by default if 'listunspent_args' not in jm_single().config.options('POLICY'): jm_single().config.set('POLICY', 'listunspent_args', '[0]') sync_wallet(wallet, fast=options.fastsync)
def main(): parser = OptionParser( usage= 'usage: %prog [options] [txid:n]', description="Adds one or more utxos to the list that can be used to make " "commitments for anti-snooping. Note that this utxo, and its " "PUBkey, will be revealed to makers, so consider the privacy " "implication. " "It may be useful to those who are having trouble making " "coinjoins due to several unsuccessful attempts (especially " "if your joinmarket wallet is new). " "'Utxo' means unspent transaction output, it must not " "already be spent. " "The options -w, -r and -R offer ways to load these utxos " "from a file or wallet. " "If you enter a single utxo without these options, you will be " "prompted to enter the private key here - it must be in " "WIF compressed format. " "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( '-r', '--read-from-file', action='store', type='str', dest='in_file', help='name of plain text csv file containing utxos, one per line, format: ' 'txid:N, WIF-compressed-privkey' ) parser.add_option( '-R', '--read-from-json', action='store', type='str', dest='in_json', help='name of json formatted file containing utxos with private keys, as ' 'output from "python wallet-tool.py -u -p walletname showutxos"' ) parser.add_option( '-w', '--load-wallet', action='store', type='str', dest='loadwallet', help='name of wallet from which to load utxos and use as commitments.' ) parser.add_option( '-g', '--gap-limit', action='store', type='int', dest='gaplimit', default = 6, help='Only to be used with -w; gap limit for Joinmarket wallet, default 6.' ) parser.add_option( '-M', '--max-mixdepth', action='store', type='int', dest='maxmixdepth', default=5, help='Only to be used with -w; number of mixdepths for wallet, default 5.' ) parser.add_option( '-d', '--delete-external', action='store_true', dest='delete_ext', help='deletes the current list of external commitment utxos', default=False ) 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() utxo_data = [] if options.delete_ext: other = options.in_file or options.in_json or options.loadwallet if len(args) > 0 or other: if raw_input("You have chosen to delete commitments, other arguments " "will be ignored; continue? (y/n)") != 'y': print "Quitting" sys.exit(0) c, e = btc.get_podle_commitments() print pformat(e) if raw_input( "You will remove the above commitments; are you sure? (y/n): ") != 'y': print "Quitting" sys.exit(0) btc.update_commitments(external_to_remove=e) print "Commitments deleted." sys.exit(0) #Three options (-w, -r, -R) for loading utxo and privkey pairs from a wallet, #csv file or json file. if options.loadwallet: os.chdir('..') #yuck (see earlier comment about package) wallet = Wallet(options.loadwallet, options.maxmixdepth, options.gaplimit) os.chdir(os.path.join(os.getcwd(), 'cmttools')) jm_single().bc_interface.sync_wallet(wallet) unsp = {} for u, av in wallet.unspent.iteritems(): addr = av['address'] key = wallet.get_key_from_addr(addr) wifkey = btc.wif_compressed_privkey(key, vbyte=get_p2pk_vbyte()) unsp[u] = {'address': av['address'], 'value': av['value'], 'privkey': wifkey} for u, pva in unsp.iteritems(): utxo_data.append((u, pva['privkey'])) elif options.in_file: with open(options.in_file, "rb") as f: utxo_info = f.readlines() for ul in utxo_info: ul = ul.rstrip() if ul: u, priv = get_utxo_info(ul) if not u: quit(parser, "Failed to parse utxo info: " + str(ul)) utxo_data.append((u, priv)) elif options.in_json: if not os.path.isfile(options.in_json): print "File: " + options.in_json + " not found." sys.exit(0) with open(options.in_json, "rb") as f: try: utxo_json = json.loads(f.read()) except: print "Failed to read json from " + options.in_json sys.exit(0) for u, pva in utxo_json.iteritems(): utxo_data.append((u, pva['privkey'])) elif len(args) == 1: 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) utxo_data.append((u, priv)) else: quit(parser, 'Invalid syntax') if options.validate or options.vonly: if not validate_utxo_data(utxo_data): quit(parser, "Utxos did not validate, quitting") if options.vonly: sys.exit(0) #We are adding utxos to the external list assert len(utxo_data) add_external_commitments(utxo_data)
noscan_methods = ['showseed', 'importprivkey'] if len(args) < 1: parser.error('Needs a wallet file or method') sys.exit(0) load_program_config() if args[0] in noseed_methods: method = args[0] else: seed = args[0] method = ('display' if len(args) == 1 else args[1].lower()) wallet = Wallet(seed, options.maxmixdepth, options.gaplimit, extend_mixdepth=not maxmixdepth_configured, storepassword=(method == 'importprivkey')) if method == 'history' and not isinstance(jm_single().bc_interface, BitcoinCoreInterface): print('showing history only available when using the Bitcoin Core ' + 'blockchain interface') sys.exit(0) if method not in noscan_methods: # if nothing was configured, we override bitcoind's options so that # unconfirmed balance is included in the wallet display by default if 'listunspent_args' not in jm_single().config.options('POLICY'): jm_single().config.set('POLICY','listunspent_args', '[0]') jm_single().bc_interface.sync_wallet(wallet) if method == 'display' or method == 'displayall' or method == 'summary':
def main(): global txfee, cjfee_a, cjfee_r, ordertype, nickserv_password, minsize, mix_levels import sys parser = OptionParser(usage='usage: %prog [options] [wallet file]') parser.add_option('-o', '--ordertype', action='store', type='string', dest='ordertype', default=ordertype, help='type of order; can be either relorder or absorder') parser.add_option('-t', '--txfee', action='store', type='int', dest='txfee', default=txfee, help='minimum miner fee in satoshis') parser.add_option('-c', '--cjfee', action='store', type='string', dest='cjfee', default='', help='requested coinjoin fee in satoshis or proportion') parser.add_option('-n', '--nickname', action='store', type='string', dest='nickname', default=jm_single().nickname, help='irc nickname') parser.add_option('-p', '--password', action='store', type='string', dest='password', default=nickserv_password, help='irc nickserv password') parser.add_option('-s', '--minsize', action='store', type='int', dest='minsize', default=minsize, help='minimum coinjoin size in satoshis') parser.add_option('-m', '--mixlevels', action='store', type='int', dest='mixlevels', default=mix_levels, help='number of mixdepths to use') (options, args) = parser.parse_args() if len(args) < 1: parser.error('Needs a wallet') sys.exit(0) seed = args[0] ordertype = options.ordertype txfee = options.txfee if ordertype == 'relorder': if options.cjfee != '': cjfee_r = options.cjfee # minimum size is such that you always net profit at least 20% of the miner fee minsize = max(int(1.2 * txfee / float(cjfee_r)), options.minsize) elif ordertype == 'absorder': if options.cjfee != '': cjfee_a = int(options.cjfee) minsize = options.minsize else: parser.error( 'You specified an incorrect order type which can be either relorder or absorder' ) sys.exit(0) if jm_single().nickname == options.nickname: jm_single().nickname = random_nick() else: jm_single().nickname = options.nickname nickserv_password = options.password mix_levels = options.mixlevels load_program_config() if isinstance(jm_single().bc_interface, BlockrInterface): c = ('\nYou are running a yield generator by polling the blockr.io ' 'website. This is quite bad for privacy. That site is owned by ' 'coinbase.com Also your bot will run faster and more efficently, ' 'you can be immediately notified of new bitcoin network ' 'information so your money will be working for you as hard as ' 'possibleLearn how to setup JoinMarket with Bitcoin Core: ' 'https://github.com/chris-belcher/joinmarket/wiki/Running' '-JoinMarket-with-Bitcoin-Core-full-node') print(c) ret = raw_input('\nContinue? (y/n):') if ret[0] != 'y': return wallet = Wallet(seed, max_mix_depth=mix_levels) jm_single().bc_interface.sync_wallet(wallet) # nickname is set way above # nickname log.debug('starting yield generator') irc = IRCMessageChannel( jm_single().nickname, realname='btcint=' + jm_single().config.get("BLOCKCHAIN", "blockchain_source"), password=nickserv_password) maker = YieldGenerator(irc, wallet) try: log.debug('connecting to irc') irc.run() except: log.debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) debug_dump_object(maker) debug_dump_object(irc) import traceback log.debug(traceback.format_exc())
def main(): parser = OptionParser( usage='usage: %prog [options] [wallet file] [destaddr(s)...]', description= 'Sends bitcoins to many different addresses using coinjoin in' ' an attempt to break the link between them. Sending to multiple ' ' addresses is highly recommended for privacy. This tumbler can' ' be configured to ask for more address mid-run, giving the user' ' a chance to click `Generate New Deposit Address` on whatever service' ' they are using.') parser.add_option( '-m', '--mixdepthsource', type='int', dest='mixdepthsrc', help= 'Mixing depth to spend from. Useful if a previous tumbler run prematurely ended with ' + 'coins being left in higher mixing levels, this option can be used to resume without needing' + ' to send to another address. default=0', default=0) parser.add_option( '-f', '--txfee', action='store', type='int', dest='txfee', default=5000, help= 'number of satoshis per participant to use as the initial estimate ' + 'for the total transaction fee, default=5000, note that this is adjusted ' + 'based on the estimated fee calculated after tx construction, based on ' + 'policy set in joinmarket.cfg.') parser.add_option( '-a', '--addrcount', type='int', dest='addrcount', default=3, help= 'How many destination addresses in total should be used. If not enough are given' ' as command line arguments, the script will ask for more. This parameter is required' ' to stop amount correlation. default=3') parser.add_option( '-x', '--maxcjfee', type='float', dest='maxcjfee', nargs=2, default=(0.01, 10000), help='maximum coinjoin fee and bitcoin value the tumbler is ' 'willing to pay to a single market maker. Both values need to be exceeded, so if ' 'the fee is 30% but only 500satoshi is paid the tx will go ahead. default=0.01, 10000 (1%, 10000satoshi)' ) parser.add_option( '-N', '--makercountrange', type='float', nargs=2, action='store', dest='makercountrange', help= 'Input the mean and spread of number of makers to use. e.g. 3 1.5 will be a normal distribution ' 'with mean 3 and standard deveation 1.5 inclusive, default=3 1.5', default=(3, 1.5)) parser.add_option( '--minmakercount', type='int', dest='minmakercount', default=2, help= 'The minimum maker count in a transaction, random values below this are clamped at this number. default=2' ) parser.add_option('-M', '--mixdepthcount', type='int', dest='mixdepthcount', help='How many mixing depths to mix through', default=4) parser.add_option( '-c', '--txcountparams', type='float', nargs=2, dest='txcountparams', default=(4, 1), help= 'The number of transactions to take coins from one mixing depth to the next, it is' ' randomly chosen following a normal distribution. Should be similar to --addrask. ' 'This option controls the parameters of the normal distribution curve. (mean, standard deviation). default=(4, 1)' ) parser.add_option( '--mintxcount', type='int', dest='mintxcount', default=1, help='The minimum transaction count per mixing level, default=1') parser.add_option( '--donateamount', type='float', dest='donateamount', default=0, help= 'percent of funds to donate to joinmarket development, or zero to opt out (default=0%)' ) parser.add_option( '--amountpower', type='float', dest='amountpower', default=100.0, help= 'The output amounts follow a power law distribution, this is the power, default=100.0' ) parser.add_option( '-l', '--timelambda', type='float', dest='timelambda', default=30, help= 'Average the number of minutes to wait between transactions. Randomly chosen ' ' following an exponential distribution, which describes the time between uncorrelated' ' events. default=30') parser.add_option( '-w', '--wait-time', action='store', type='float', dest='waittime', help='wait time in seconds to allow orders to arrive, default=20', default=20) parser.add_option( '-s', '--mincjamount', type='int', dest='mincjamount', default=100000, help='minimum coinjoin amount in transaction in satoshi, default 100k') parser.add_option( '-q', '--liquiditywait', type='int', dest='liquiditywait', default=60, help= 'amount of seconds to wait after failing to choose suitable orders before trying again, default 60' ) parser.add_option( '--maxbroadcasts', type='int', dest='maxbroadcasts', default=4, help= 'maximum amount of times to broadcast a transaction before giving up and re-creating it, default 4' ) parser.add_option( '--maxcreatetx', type='int', dest='maxcreatetx', default=9, help= 'maximum amount of times to re-create a transaction before giving up, default 9' ) (options, args) = parser.parse_args() if len(args) < 1: parser.error('Needs a wallet file') sys.exit(0) wallet_file = args[0] destaddrs = args[1:] print(destaddrs) load_program_config() for addr in destaddrs: addr_valid, errormsg = validate_address(addr) if not addr_valid: print('ERROR: Address ' + addr + ' invalid. ' + errormsg) return if len(destaddrs) > options.addrcount: options.addrcount = len(destaddrs) if options.addrcount + 1 > options.mixdepthcount: print('not enough mixing depths to pay to all destination addresses, ' 'increasing mixdepthcount') options.mixdepthcount = options.addrcount + 1 if options.donateamount > 10.0: # fat finger probably, or misunderstanding options.donateamount = 0.9 print(str(options)) tx_list = generate_tumbler_tx(destaddrs, options) if not tx_list: return tx_list2 = copy.deepcopy(tx_list) tx_dict = {} for tx in tx_list2: srcmixdepth = tx['srcmixdepth'] tx.pop('srcmixdepth') if srcmixdepth not in tx_dict: tx_dict[srcmixdepth] = [] tx_dict[srcmixdepth].append(tx) dbg_tx_list = [] for srcmixdepth, txlist in tx_dict.iteritems(): dbg_tx_list.append({'srcmixdepth': srcmixdepth, 'tx': txlist}) log.debug('tumbler transaction list') pprint(dbg_tx_list) total_wait = sum([tx['wait'] for tx in tx_list]) print('creates ' + str(len(tx_list)) + ' transactions in total') print('waits in total for ' + str(len(tx_list)) + ' blocks and ' + str(total_wait) + ' minutes') total_block_and_wait = len(tx_list) * 10 + total_wait print('estimated time taken ' + str(total_block_and_wait) + ' minutes or ' + str(round(total_block_and_wait / 60.0, 2)) + ' hours') if options.addrcount <= 1: print('=' * 50) print('WARNING: You are only using one destination address') print('this is very bad for privacy') print('=' * 50) ret = raw_input('tumble with these tx? (y/n):') if ret[0] != 'y': return # NOTE: possibly out of date documentation # a couple of modes # im-running-from-the-nsa, takes about 80 hours, costs a lot # python tumbler.py -a 10 -N 10 5 -c 10 5 -l 50 -M 10 wallet_file 1xxx # # quick and cheap, takes about 90 minutes # python tumbler.py -N 2 1 -c 3 0.001 -l 10 -M 3 -a 1 wallet_file 1xxx # # default, good enough for most, takes about 5 hours # python tumbler.py wallet_file 1xxx # # for quick testing # python tumbler.py -N 2 1 -c 3 0.001 -l 0.1 -M 3 -a 0 wallet_file 1xxx 1yyy wallet = Wallet(wallet_file, max_mix_depth=options.mixdepthsrc + options.mixdepthcount) jm_single().bc_interface.sync_wallet(wallet) jm_single().nickname = random_nick() log.debug('starting tumbler') irc = IRCMessageChannel(jm_single().nickname) tumbler = Tumbler(irc, wallet, tx_list, options) try: log.debug('connecting to irc') irc.run() except: log.debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) debug_dump_object(tumbler) debug_dump_object(tumbler.cjtx) import traceback log.debug(traceback.format_exc())
noscan_methods = ['showseed', 'importprivkey'] if len(args) < 1: parser.error('Needs a wallet file or method') sys.exit(0) load_program_config() if args[0] in noseed_methods: method = args[0] else: seed = args[0] method = ('display' if len(args) == 1 else args[1].lower()) wallet = Wallet(seed, options.maxmixdepth, options.gaplimit, extend_mixdepth=not maxmixdepth_configured, storepassword=(method == 'importprivkey')) if method == 'history' and not isinstance(jm_single().bc_interface, BitcoinCoreInterface): print('showing history only available when using the Bitcoin Core ' + 'blockchain interface') sys.exit(0) if method not in noscan_methods: # if nothing was configured, we override bitcoind's options so that # unconfirmed balance is included in the wallet display by default if 'listunspent_args' not in jm_single().config.options('POLICY'): jm_single().config.set('POLICY','listunspent_args', '[0]') jm_single().bc_interface.sync_wallet(wallet) if method == 'showutxos':
def main(): parser = OptionParser( usage= 'usage: %prog [options] [wallet file] [[dest..] [amount]..]', description='Sends a payment from your wallet to an given address' + ' using coinjoin but for users who dont mind ' + 'waiting. First acts as a maker, announcing an order' + ' and waiting for someone to fill it. After a set ' + 'period of time, gives up waiting and acts as a taker' + ' and coinjoins any remaining coins. Is able to send' + ' to multiple locations one after another. [dest] ' + 'can be multiple addresses or a xpub BIP32 key. xpub' + ' keys can be optionally followed with :index to ' + 'start from another address than zero') parser.add_option( '-f', '--txfee', action='store', type='int', dest='txfee', default=1000, help='miner fee contribution, in satoshis, default=1000') parser.add_option( '-N', '--makercount', action='store', type='int', dest='makercount', help='how many makers to coinjoin with, default random ' 'from 5 to 7', default=random.randint(5, 7)) parser.add_option( '-w', '--wait-time', action='store', type='float', dest='waittime', help='wait time in hours as a maker before becoming a taker, ' + 'or zero to wait forever, default=8', default=8) parser.add_option( '-c', '--base-cjfee', action='store', type='int', dest='cjfee_base', help= 'base coinjoin fee asked for when being a maker, in satoshis per' + ' order filled, default=500', default=500) parser.add_option( '-a', '--add-cjfee', action='store', type='int', dest='cjfee_add', help= 'additional coinjoin fee asked for when being a maker when ' + 'coinjoin amount not exact, in satoshis per order filled' + ', default=1000', default=1000) parser.add_option( '-m', '--mixdepth', action='store', type='int', dest='mixdepth', help='mixing depth to spend from, default=0', default=0) parser.add_option( '--rpcwallet', action='store_true', dest='userpcwallet', default=False, help= 'Use the Bitcoin Core wallet through json rpc, instead of the ' + 'internal joinmarket wallet. Requires blockchain_source=json-rpc.' + ' NOT IMPLEMENTED YET') parser.add_option('--fast', action='store_true', dest='fastsync', default=False, help=('choose to do fast wallet sync, only for Core and ' 'only for previously synced wallet')) parser.add_option( '-x', '--maxcjfee', type='float', dest='maxcjfee', nargs=2, default=(0.01, 10000), help='maximum coinjoin fee and bitcoin value the taker is ' + 'willing to pay to a single market maker. Both values need' + ' to be exceeded, so if the fee is 30% but only 500satoshi ' + 'is paid the tx will go ahead. default=0.01, 10000 ' + '(1%, 10000satoshi)') parser.add_option( '-q', '--liquiditywait', type='int', dest='liquiditywait', default=20, help= 'amount of seconds to wait after failing to choose suitable orders' ' before trying again, default 20') parser.add_option( '-u', '--minoutputsize', type='int', dest='minoutputsize', nargs=1, default=30000, help='minimum size of output in satoshis produced by ' 'patientsendpayment. default=30000 satoshi') (options, args) = parser.parse_args() if len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) wallet_name = args[0] load_program_config() send_jobs = [] destination = None for ar in args[1:]: if ar.isdigit(): if destination == None: log.error('found amount without destination') return elif isinstance(destination, list): send_jobs.append( {'amount': int(ar), 'addresses': destination, 'index': 0} ) elif isinstance(destination, tuple): send_jobs.append( {'amount': int(ar), 'xpub': destination[0] , 'index': destination[1]} ) else: assert False destination = None else: if validate_address(ar)[0]: if destination == None: destination = [] destination.append(ar) else: index = 0 colon = ar.find(':') if colon > -1: index = int(ar[colon+1:]) ar = ar[:colon] if is_bip32_pubkey(ar): destination = (ar, index) else: log.error('unable to parse destination: ' + ar) return if destination != None: log.error('missing amount') return for j in send_jobs: print('sending ' + str(j['amount']) + ' satoshi to: ') if 'addresses' in j: for a in j['addresses']: print(' ' + get_next_address(j)) else: print(' ' + j['xpub'] + '\n starting from index: ' + str(j['index']) + '. first 5 addresses:') index_cache = j['index'] for i in range(5): print(' ' + get_next_address(j)) j['index'] = index_cache waittime = timedelta(hours=options.waittime).total_seconds() # todo: this section doesn't make a lot of sense if not options.userpcwallet: wallet = Wallet(wallet_name, options.mixdepth + 1) else: print 'not implemented yet' sys.exit(0) # wallet = BitcoinCoreWallet(fromaccount=wallet_name) sync_wallet(wallet, fast=options.fastsync) available_balance = wallet.get_balance_by_mixdepth()[options.mixdepth] total_amount = sum((j['amount'] for j in send_jobs)) if available_balance < total_amount: print 'not enough money at mixdepth=%d, exiting' % options.mixdepth return log.info('Running patient sender of a payment') mcs = [IRCMessageChannel(c) for c in get_irc_mchannels()] mcc = MessageChannelCollection(mcs) PatientSendPayment(mcc, wallet, send_jobs, options, waittime) try: mcc.run() except: log.warn('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) # todo: looks wrong. dump on the class object? # debug_dump_object(taker) import traceback traceback.print_exc()
def main(): parser = OptionParser( usage= 'usage: %prog [options] [wallet file / fromaccount] [amount] [destaddr]', description='Sends a single payment from a given mixing depth of your ' + 'wallet to an given address using coinjoin and then switches off. Also sends from bitcoinqt. ' + 'Setting amount to zero will do a sweep, where the entire mix depth is emptied' ) parser.add_option( '-f', '--txfee', action='store', type='int', dest='txfee', default=-1, help= 'number of satoshis per participant to use as the initial estimate ' + 'for the total transaction fee, default=dynamically estimated, note that this is adjusted ' + 'based on the estimated fee calculated after tx construction, based on ' + 'policy set in joinmarket.cfg.') parser.add_option( '-w', '--wait-time', action='store', type='float', dest='waittime', help='wait time in seconds to allow orders to arrive, default=15', default=15) parser.add_option('-N', '--makercount', action='store', type='int', dest='makercount', help='how many makers to coinjoin with, default random ' 'from 5 to 7; use 0 to send *direct* to a destination ' 'address, not using Joinmarket', default=random.randint(5, 7)) parser.add_option( '-C', '--choose-cheapest', action='store_true', dest='choosecheapest', default=False, help= 'override weightened offers picking and choose cheapest. this might reduce anonymity.' ) 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('-m', '--mixdepth', action='store', type='int', dest='mixdepth', help='mixing depth to spend from, default=0', default=0) parser.add_option('-a', '--amtmixdepths', action='store', type='int', dest='amtmixdepths', help='number of mixdepths in wallet, default 5', default=5) parser.add_option('-g', '--gap-limit', type="int", action='store', dest='gaplimit', help='gap limit for wallet, default=6', default=6) parser.add_option('--yes', action='store_true', dest='answeryes', default=False, help='answer yes to everything') parser.add_option( '--rpcwallet', action='store_true', dest='userpcwallet', default=False, help=('Use the Bitcoin Core wallet through json rpc, instead ' 'of the internal joinmarket wallet. Requires ' 'blockchain_source=json-rpc')) parser.add_option('--fast', action='store_true', dest='fastsync', default=False, help=('choose to do fast wallet sync, only for Core and ' 'only for previously synced wallet')) (options, args) = parser.parse_args() if len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) wallet_name = args[0] amount = int(args[1]) destaddr = args[2] load_program_config() addr_valid, errormsg = validate_address(destaddr) if not addr_valid: print('ERROR: Address invalid. ' + errormsg) return chooseOrdersFunc = None if options.pickorders: chooseOrdersFunc = pick_order if amount == 0: print 'WARNING: You may have to pick offers multiple times' print 'WARNING: due to manual offer picking while sweeping' elif options.choosecheapest: chooseOrdersFunc = cheapest_order_choose else: # choose randomly (weighted) chooseOrdersFunc = weighted_order_choose # Dynamically estimate a realistic fee if it currently is the default value. # At this point we do not know even the number of our own inputs, so # we guess conservatively with 2 inputs and 2 outputs each if options.txfee == -1: options.txfee = max(options.txfee, estimate_tx_fee(2, 2)) log.info("Estimated miner/tx fee for each cj participant: " + str(options.txfee)) assert (options.txfee >= 0) log.info('starting sendpayment') #If we are not direct sending, then minimum_maker setting should #not be larger than the requested number of counterparties if options.makercount != 0 and options.makercount < jm_single( ).config.getint("POLICY", "minimum_makers"): log.error("You selected a number of counterparties (" + \ str(options.makercount) + \ ") less than the " "minimum requirement (" + \ str(jm_single().config.getint("POLICY","minimum_makers")) + \ "); you can edit the value 'minimum_makers'" " in the POLICY section in joinmarket.cfg to correct this. " "Quitting.") exit(0) if not options.userpcwallet: max_mix_depth = max([options.mixdepth, options.amtmixdepths]) wallet = Wallet(wallet_name, max_mix_depth, options.gaplimit) else: wallet = BitcoinCoreWallet(fromaccount=wallet_name) sync_wallet(wallet, fast=options.fastsync) if options.makercount == 0: direct_send(wallet, amount, options.mixdepth, destaddr) return mcs = [IRCMessageChannel(c) for c in get_irc_mchannels()] mcc = MessageChannelCollection(mcs) log.info("starting sendpayment") taker = SendPayment(mcc, wallet, destaddr, amount, options.makercount, options.txfee, options.waittime, options.mixdepth, options.answeryes, chooseOrdersFunc) try: log.info('starting message channels') mcc.run() except: log.warn('Quitting! Dumping object contents to logfile.') debug_dump_object(wallet, ['addr_cache', 'keys', 'wallet_name', 'seed']) debug_dump_object(taker) import traceback log.debug(traceback.format_exc())
if len(args) < 1: parser.error("Needs a wallet file or method") sys.exit(0) load_program_config() if args[0] in noseed_methods: method = args[0] else: seed = args[0] method = "display" if len(args) == 1 else args[1].lower() wallet = Wallet( seed, options.maxmixdepth, options.gaplimit, extend_mixdepth=not maxmixdepth_configured, storepassword=(method == "importprivkey"), ) if method not in noscan_methods: # if nothing was configured, we override bitcoind's options so that # unconfirmed balance is included in the wallet display by default if "listunspent_args" not in jm_single().config.options("POLICY"): jm_single().config.set("POLICY", "listunspent_args", "[0]") jm_single().bc_interface.sync_wallet(wallet) if method == "display" or method == "displayall" or method == "summary": def cus_print(s): if method != "summary": print(s)
def main(): parser = OptionParser( usage= 'usage: %prog [options] [wallet file / fromaccount] [amount] [destaddr]', description='Sends a payment from your wallet to an given address' + ' using coinjoin. First acts as a maker, announcing an order and ' + 'waiting for someone to fill it. After a set period of time, gives' + ' up waiting and acts as a taker and coinjoins any remaining coins') parser.add_option( '-f', '--txfee', action='store', type='int', dest='txfee', default=10000, help='miner fee contribution, in satoshis, default=10000') parser.add_option( '-N', '--makercount', action='store', type='int', dest='makercount', help= 'how many makers to coinjoin with when taking liquidity, default=2', default=2) parser.add_option( '-w', '--wait-time', action='store', type='float', dest='waittime', help='wait time in hours as a maker before becoming a taker, default=8', default=8) parser.add_option( '-c', '--cjfee', action='store', type='int', dest='cjfee', help= 'coinjoin fee asked for when being a maker, in satoshis per order filled, default=50000', default=50000) parser.add_option( '-m', '--mixdepth', action='store', type='int', dest='mixdepth', help='mixing depth to spend from, default=0', default=0) parser.add_option( '--rpcwallet', action='store_true', dest='userpcwallet', default=False, help= 'Use the Bitcoin Core wallet through json rpc, instead of the internal joinmarket ' + 'wallet. Requires blockchain_source=json-rpc') (options, args) = parser.parse_args() if len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) wallet_name = args[0] amount = int(args[1]) destaddr = args[2] load_program_config() addr_valid, errormsg = validate_address(destaddr) if not addr_valid: print 'ERROR: Address invalid. ' + errormsg return waittime = timedelta(hours=options.waittime).total_seconds() print 'txfee=%d cjfee=%d waittime=%s makercount=%d' % ( options.txfee, options.cjfee, str(timedelta(hours=options.waittime)), options.makercount) # todo: this section doesn't make a lot of sense if not options.userpcwallet: wallet = Wallet(wallet_name, options.mixdepth + 1) else: print 'not implemented yet' sys.exit(0) # wallet = BitcoinCoreWallet(fromaccount=wallet_name) jm_single().bc_interface.sync_wallet(wallet) available_balance = wallet.get_balance_by_mixdepth()[options.mixdepth] if available_balance < amount: print 'not enough money at mixdepth=%d, exiting' % options.mixdepth return jm_single().nickname = random_nick() log.debug('Running patient sender of a payment') irc = IRCMessageChannel(jm_single().nickname) PatientSendPayment(irc, wallet, destaddr, amount, options.makercount, options.txfee, options.cjfee, waittime, options.mixdepth) try: irc.run() except: log.debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) # todo: looks wrong. dump on the class object? # debug_dump_object(taker) import traceback traceback.print_exc()
def main(): parser = OptionParser( usage= 'usage: %prog [options] [wallet file / fromaccount] [amount] [destaddr]', description='Sends a payment from your wallet to an given address' + ' using coinjoin. First acts as a maker, announcing an order and ' + 'waiting for someone to fill it. After a set period of time, gives' + ' up waiting and acts as a taker and coinjoins any remaining coins.' + ' NOTE: In the current state of JoinMarket software, this script' + ' only works if your JoinMarket wallet contains the private key of your' + ' destination address. So you can only send to yourself and you need' + ' to import the privkey') parser.add_option( '-f', '--txfee', action='store', type='int', dest='txfee', default=10000, help='miner fee contribution, in satoshis, default=10000') parser.add_option( '-N', '--makercount', action='store', type='int', dest='makercount', help= 'how many makers to coinjoin with when taking liquidity, default=2', default=2) parser.add_option( '-w', '--wait-time', action='store', type='float', dest='waittime', help='wait time in hours as a maker before becoming a taker, default=8', default=8) parser.add_option( '-c', '--cjfee', action='store', type='int', dest='cjfee', help= 'coinjoin fee asked for when being a maker, in satoshis per order filled, default=50000', default=50000) parser.add_option('-m', '--mixdepth', action='store', type='int', dest='mixdepth', help='mixing depth to spend from, default=0', default=0) parser.add_option( '--rpcwallet', action='store_true', dest='userpcwallet', default=False, help= 'Use the Bitcoin Core wallet through json rpc, instead of the internal joinmarket ' + 'wallet. Requires blockchain_source=json-rpc') (options, args) = parser.parse_args() if len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) wallet_name = args[0] amount = int(args[1]) destaddr = args[2] load_program_config() addr_valid, errormsg = validate_address(destaddr) if not addr_valid: print 'ERROR: Address invalid. ' + errormsg return waittime = timedelta(hours=options.waittime).total_seconds() print 'txfee=%d cjfee=%d waittime=%s makercount=%d' % ( options.txfee, options.cjfee, str( timedelta(hours=options.waittime)), options.makercount) # todo: this section doesn't make a lot of sense if not options.userpcwallet: wallet = Wallet(wallet_name, options.mixdepth + 1) else: print 'not implemented yet' sys.exit(0) # wallet = BitcoinCoreWallet(fromaccount=wallet_name) jm_single().bc_interface.sync_wallet(wallet) available_balance = wallet.get_balance_by_mixdepth()[options.mixdepth] if available_balance < amount: print 'not enough money at mixdepth=%d, exiting' % options.mixdepth return jm_single().nickname = random_nick() log.debug('Running patient sender of a payment') irc = IRCMessageChannel(jm_single().nickname) PatientSendPayment(irc, wallet, destaddr, amount, options.makercount, options.txfee, options.cjfee, waittime, options.mixdepth) try: irc.run() except: log.debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) # todo: looks wrong. dump on the class object? # debug_dump_object(taker) import traceback traceback.print_exc()
noscan_methods = ['changepassphrase', 'showseed', 'importprivkey', 'dumpprivkey'] if len(args) < 1: parser.error('Needs a wallet file or method') sys.exit(0) load_program_config() if args[0] in noseed_methods: method = args[0] else: seed = args[0] method = ('display' if len(args) == 1 else args[1].lower()) wallet = Wallet(seed, options.maxmixdepth, options.gaplimit, extend_mixdepth=not maxmixdepth_configured, storepassword=(method == 'importprivkey')) if method == 'history' and not isinstance(jm_single().bc_interface, BitcoinCoreInterface): print('showing history only available when using the Bitcoin Core ' + 'blockchain interface') sys.exit(0) if method not in noscan_methods: # if nothing was configured, we override bitcoind's options so that # unconfirmed balance is included in the wallet display by default if 'listunspent_args' not in jm_single().config.options('POLICY'): jm_single().config.set('POLICY','listunspent_args', '[0]') sync_wallet(wallet, fast=options.fastsync)