Example #1
0
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
Example #2
0
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
Example #3
0
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    
Example #4
0
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())
Example #5
0
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())
Example #7
0
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
Example #8
0
    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)
Example #9
0
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)
Example #11
0
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())
Example #12
0
]

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)
Example #13
0
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)
Example #14
0
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())
Example #16
0
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())
Example #17
0
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':
Example #18
0
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()
Example #19
0
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())
Example #20
0
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)
Example #21
0
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()
Example #23
0
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()
Example #24
0
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)