Esempio n. 1
0
def main():
    jm_single().nickname = random_nick(
    )  # watcher' +binascii.hexlify(os.urandom(4))
    load_program_config()

    parser = OptionParser(
        usage='usage: %prog [options]',
        description='Runs a webservice which shows the orderbook.')
    parser.add_option('-H',
                      '--host',
                      action='store',
                      type='string',
                      dest='host',
                      default='localhost',
                      help='hostname or IP to bind to, default=localhost')
    parser.add_option('-p',
                      '--port',
                      action='store',
                      type='int',
                      dest='port',
                      help='port to listen on, default=62601',
                      default=62601)
    (options, args) = parser.parse_args()

    hostport = (options.host, options.port)

    irc = IRCMessageChannel(jm_single().nickname)

    # todo: is the call to GUITaker needed, or the return. taker unused
    taker = GUITaker(irc, hostport)
    print('starting irc')

    irc.run()
Esempio n. 2
0
def main():
    jm_single().nickname = random_nick()  # watcher' +binascii.hexlify(os.urandom(4))
    load_program_config()

    parser = OptionParser(
            usage='usage: %prog [options]',
            description='Runs a webservice which shows the orderbook.')
    parser.add_option('-H',
                      '--host',
                      action='store',
                      type='string',
                      dest='host',
                      default='localhost',
                      help='hostname or IP to bind to, default=localhost')
    parser.add_option('-p',
                      '--port',
                      action='store',
                      type='int',
                      dest='port',
                      help='port to listen on, default=62601',
                      default=62601)
    (options, args) = parser.parse_args()

    hostport = (options.host, options.port)

    irc = IRCMessageChannel(jm_single().nickname)

    # todo: is the call to GUITaker needed, or the return. taker unused
    taker = GUITaker(irc, hostport)
    print('starting irc')

    irc.run()
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())
Esempio n. 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())
Esempio n. 5
0
def main():
    load_program_config()

    parser = OptionParser(
        usage='usage: %prog [options]',
        description='Runs a webservice which shows the orderbook.')
    parser.add_option('-H',
                      '--host',
                      action='store',
                      type='string',
                      dest='host',
                      default='localhost',
                      help='hostname or IP to bind to, default=localhost')
    parser.add_option('-p',
                      '--port',
                      action='store',
                      type='int',
                      dest='port',
                      help='port to listen on, default=62601',
                      default=62601)
    (options, args) = parser.parse_args()

    hostport = (options.host, options.port)
    mcs = [IRCMessageChannel(c) for c in get_irc_mchannels()]
    mcc = MessageChannelCollection(mcs)
    log.info("Starting ob-watcher")
    # todo: is the call to GUITaker needed, or the return. taker unused
    taker = GUITaker(mcc, hostport)
    print('starting irc')

    mcc.run()
Esempio n. 6
0
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 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())
Esempio n. 8
0
def main():
    from socket import gethostname
    nickname = 'cj-maker-' + btc.sha256(gethostname())[:6]
    import sys
    seed = sys.argv[
        1]  # btc.sha256('dont use brainwallets except for holding testnet coins')

    load_program_config()
    wallet = Wallet(seed, max_mix_depth=5)
    jm_single().bc_interface.sync_wallet(wallet)

    irc = IRCMessageChannel(nickname)
    maker = Maker(irc, wallet)
    try:
        print('connecting to irc')
        irc.run()
    except:
        log.debug('CRASHING, DUMPING EVERYTHING')
        log.debug('wallet seed = ' + seed)
        debug_dump_object(wallet, ['addr_cache'])
        debug_dump_object(maker)
        import traceback
        traceback.print_exc()
Esempio n. 9
0
def main():
    parser = OptionParser(
        usage=
        'usage: %prog [options] [tx hex]',
        description='Sends a transaction to a random market maker requesting that they broadcast it '
        +
        'to the wider bitcoin network. Used to add a layer between your own IP address and the network '
        +
        'where other methods are not possible.')
    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=10)
    (options, args) = parser.parse_args()

    if len(args) < 1:
        parser.error('Needs a transaction hex string')
        sys.exit(0)
    txhex = args[0]

    load_program_config()
    jm_single().nickname = random_nick()
    log.debug('starting broadcast-tx')
    irc = IRCMessageChannel(jm_single().nickname)
    taker = Broadcaster(irc, options.waittime, txhex)
    try:
        log.debug('starting irc')
        irc.run()
    except:
        log.debug('CRASHING, DUMPING EVERYTHING')
        debug_dump_object(taker)
        import traceback
        log.debug(traceback.format_exc())
Esempio n. 10
0
def main():
    parser = OptionParser(
        usage='usage: %prog [options] [tx hex]',
        description=
        'Sends a transaction to a random market maker requesting that they broadcast it '
        +
        'to the wider bitcoin network. Used to add a layer between your own IP address and the network '
        + 'where other methods are not possible.')
    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=10)
    (options, args) = parser.parse_args()

    if len(args) < 1:
        parser.error('Needs a transaction hex string')
        sys.exit(0)
    txhex = args[0]

    load_program_config()
    jm_single().nickname = random_nick()
    log.debug('starting broadcast-tx')
    irc = IRCMessageChannel(jm_single().nickname)
    taker = Broadcaster(irc, options.waittime, txhex)
    try:
        log.debug('starting irc')
        irc.run()
    except:
        log.debug('CRASHING, DUMPING EVERYTHING')
        debug_dump_object(taker)
        import traceback
        log.debug(traceback.format_exc())
Esempio n. 11
0
def main():
    from socket import gethostname
    nickname = 'cj-maker-' + btc.sha256(gethostname())[:6]
    import sys
    seed = sys.argv[
        1
    ]  # btc.sha256('dont use brainwallets except for holding testnet coins')

    load_program_config()
    wallet = Wallet(seed, max_mix_depth=5)
    jm_single().bc_interface.sync_wallet(wallet)

    irc = IRCMessageChannel(nickname)
    maker = Maker(irc, wallet)
    try:
        print('connecting to irc')
        irc.run()
    except:
        log.debug('CRASHING, DUMPING EVERYTHING')
        log.debug('wallet seed = ' + seed)
        debug_dump_object(wallet, ['addr_cache'])
        debug_dump_object(maker)
        import traceback
        traceback.print_exc()
Esempio n. 12
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())
Esempio n. 13
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())
Esempio n. 14
0
def test_sendpayment(setup_regtest, num_ygs, wallet_structures, mean_amt,
                     mixdepth, sending_amt):
    """Test of sendpayment code, with yield generators in background.
    """
    log = get_log()
    makercount = num_ygs
    answeryes = True
    txfee = 5000
    waittime = 5
    amount = sending_amt
    wallets = make_wallets(makercount + 1,
                           wallet_structures=wallet_structures,
                           mean_amt=mean_amt)
    #the sendpayment bot uses the last wallet in the list
    wallet = wallets[makercount]['wallet']

    yigen_procs = []
    for i in range(makercount):
        ygp = local_command([python_cmd, yg_cmd,\
                             str(wallets[i]['seed'])], bg=True)
        time.sleep(2)  #give it a chance
        yigen_procs.append(ygp)

    #A significant delay is needed to wait for the yield generators to sync
    time.sleep(20)
    if btc.secp_present:
        destaddr = btc.privkey_to_address(os.urandom(32),
                                          from_hex=False,
                                          magicbyte=get_p2pk_vbyte())
    else:
        destaddr = btc.privkey_to_address(os.urandom(32),
                                          magicbyte=get_p2pk_vbyte())

    addr_valid, errormsg = validate_address(destaddr)
    assert addr_valid, "Invalid destination address: " + destaddr + \
           ", error message: " + errormsg

    #TODO paramatetrize this as a test variable
    chooseOrdersFunc = weighted_order_choose

    jm_single().nickname = random_nick()

    log.debug('starting sendpayment')

    jm_single().bc_interface.sync_wallet(wallet)

    #Trigger PING LAG sending artificially
    joinmarket.irc.PING_INTERVAL = 3

    irc = IRCMessageChannel(jm_single().nickname)
    taker = sendpayment.SendPayment(irc, wallet, destaddr, amount, makercount,
                                    txfee, waittime, mixdepth, answeryes,
                                    chooseOrdersFunc)
    try:
        log.debug('starting irc')
        irc.run()
    finally:
        if any(yigen_procs):
            for ygp in yigen_procs:
                #NB *GENTLE* shutdown is essential for
                #test coverage reporting!
                ygp.send_signal(signal.SIGINT)
                ygp.wait()
    #wait for block generation
    time.sleep(5)
    received = jm_single().bc_interface.get_received_by_addr(
        [destaddr], None)['data'][0]['balance']
    if amount != 0:
        assert received == amount, "sendpayment failed - coins not arrived, " +\
           "received: " + str(received)
    #TODO: how to check success for sweep case?
    else:
        assert received != 0
Esempio n. 15
0
def test_tumbler(setup_tumbler, num_ygs, wallet_structures, mean_amt, sdev_amt,
                 yg_excess):
    """Test of tumbler code, with yield generators in background.
    """
    log = get_log()
    options = Options()
    options.mixdepthsrc = 0
    options.mixdepthcount = 4
    options.minmakercount = 2
    options.makercountrange = (num_ygs, 0)
    options.maxcjfee = (0.01, 10000)
    options.txfee = 5000
    options.addrcount = 3
    options.donateamount = 0.5
    options.txcountparams = (4, 1)
    options.mintxcount = 1
    options.amountpower = 100
    options.timelambda = 0.2
    options.waittime = 10
    options.mincjamount = 1000000
    options.liquiditywait = 5
    options.maxbroadcasts = 4
    options.maxcreatetx = 9
    options = vars(options)

    wallets = make_wallets(num_ygs + 1,
                           wallet_structures=wallet_structures,
                           mean_amt=mean_amt,
                           sdev_amt=sdev_amt)
    #need to make sure that at least some ygs have substantially
    #more coins for last stages of sweep/spend in tumble:
    for i in range(num_ygs):
        jm_single().bc_interface.grab_coins(
            wallets[i]['wallet'].get_external_addr(0), yg_excess)
    #the tumbler bot uses the last wallet in the list
    wallet = wallets[num_ygs]['wallet']

    yigen_procs = []
    for i in range(num_ygs):
        ygp = local_command([python_cmd, yg_cmd,\
                             str(wallets[i]['seed'])], bg=True)
        time.sleep(2)  #give it a chance
        yigen_procs.append(ygp)

    #A significant delay is needed to wait for the yield generators to sync
    time.sleep(20)
    destaddrs = []
    for i in range(3):
        destaddr = btc.privkey_to_address(os.urandom(32),
                                          from_hex=False,
                                          magicbyte=get_p2pk_vbyte())
        addr_valid, errormsg = validate_address(destaddr)
        assert addr_valid, "Invalid destination address: " + destaddr + \
                   ", error message: " + errormsg
        destaddrs.append(destaddr)
    tx_list = tumbler.generate_tumbler_tx(destaddrs, options)
    pprint(tx_list)
    if options['addrcount'] + 1 > options['mixdepthcount']:
        print(
            'not enough mixing depths to pay to all destination addresses, '
            'increasing mixdepthcount')
        options['mixdepthcount'] = options['addrcount'] + 1

    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')

    jm_single().nickname = random_nick()

    log.debug('starting tumbler')

    jm_single().bc_interface.sync_wallet(wallet)
    jm_single().bc_interface.pushtx_failure_prob = 0.4
    mcs = [
        IRCMessageChannel(c,
                          jm_single().nickname) for c in get_irc_mchannels()
    ]
    mcc = MessageChannelCollection(mcs)
    tumbler_bot = tumbler.Tumbler(mcc, wallet, tx_list, options)
    try:
        log.debug('starting message channels')
        mcc.run()
    except:
        log.debug('CRASHING, DUMPING EVERYTHING')
        debug_dump_object(wallet,
                          ['addr_cache', 'keys', 'wallet_name', 'seed'])
        debug_dump_object(tumbler_bot)
        import traceback
        log.debug(traceback.format_exc())
    finally:
        if any(yigen_procs):
            for ygp in yigen_procs:
                #NB *GENTLE* shutdown is essential for
                #test coverage reporting!
                ygp.send_signal(signal.SIGINT)
                ygp.wait()
    #wait for block generation
    time.sleep(5)
    received = jm_single().bc_interface.get_received_by_addr(
        [destaddr], None)['data'][0]['balance']
    assert received != 0
    """TODO: figure out a sensible assertion check for the destination
Esempio n. 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')
    (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())
Esempio n. 17
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())
Esempio n. 18
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())
Esempio n. 19
0
def test_external_commitment_used(setup_podle):
    tries = jm_single().config.getint("POLICY", "taker_utxo_retries")
    #Don't want to wait too long, but must account for possible
    #throttling with !auth
    jm_single().maker_timeout_sec = 12
    amount = 50000000
    wallets = make_wallets(3,
                           wallet_structures=[[1, 0, 0, 0, 0], [1, 0, 0, 0, 0],
                                              [1, 1, 0, 0, 0]],
                           mean_amt=1)
    #the sendpayment bot uses the last wallet in the list
    wallet = wallets[2]['wallet']
    yigen_procs = []
    for i in range(2):
        ygp = local_command([python_cmd, yg_cmd,\
                             str(wallets[i]['seed'])], bg=True)
        time.sleep(2)  #give it a chance
        yigen_procs.append(ygp)

    #A significant delay is needed to wait for the yield generators to sync
    time.sleep(10)
    destaddr = btc.privkey_to_address(binascii.hexlify(os.urandom(32)),
                                      magicbyte=get_p2pk_vbyte())
    addr_valid, errormsg = validate_address(destaddr)
    assert addr_valid, "Invalid destination address: " + destaddr + \
           ", error message: " + errormsg

    log.debug('starting sendpayment')

    jm_single().bc_interface.sync_wallet(wallet)

    #Trigger PING LAG sending artificially
    joinmarket.irc.PING_INTERVAL = 3

    mcs = [IRCMessageChannel(c) for c in get_irc_mchannels()]
    mcc = MessageChannelCollection(mcs)
    #add all utxo in mixdepth 0 to 'used' list of commitments,
    utxos = wallet.get_utxos_by_mixdepth()[0]
    for u, addrval in utxos.iteritems():
        priv = wallet.get_key_from_addr(addrval['address'])
        podle = btc.PoDLE(u, priv)
        for i in range(tries):
            #loop because we want to use up all retries of this utxo
            commitment = podle.generate_podle(i)['commit']
            btc.update_commitments(commitment=commitment)

    #create a new utxo, notionally from an external source; to make life a little
    #easier we'll pay to another mixdepth, but this is OK because
    #taker does not source from here currently, only from the utxos chosen
    #for the transaction, not the whole wallet. So we can treat it as if
    #external (don't access its privkey).
    utxos = wallet.get_utxos_by_mixdepth()[1]
    ecs = {}
    for u, addrval in utxos.iteritems():
        priv = wallet.get_key_from_addr(addrval['address'])
        ecs[u] = {}
        ecs[u]['reveal'] = {}
        for j in range(tries):
            P, P2, s, e, commit = generate_single_podle_sig(
                binascii.unhexlify(priv), j)
            if 'P' not in ecs[u]:
                ecs[u]['P'] = P
            ecs[u]['reveal'][j] = {'P2': P2, 's': s, 'e': e}
    btc.update_commitments(external_to_add=ecs)
    #Now the conditions described above hold. We do a normal single
    #sendpayment.
    taker = sendpayment.SendPayment(mcc, wallet, destaddr, amount, 2, 5000, 3,
                                    0, True, weighted_order_choose)
    try:
        log.debug('starting message channels')
        mcc.run()
    finally:
        if any(yigen_procs):
            for ygp in yigen_procs:
                #NB *GENTLE* shutdown is essential for
                #test coverage reporting!
                ygp.send_signal(signal.SIGINT)
                ygp.wait()
    #wait for block generation
    time.sleep(5)
    received = jm_single().bc_interface.get_received_by_addr(
        [destaddr], None)['data'][0]['balance']
    assert received == amount, "sendpayment failed - coins not arrived, " +\
           "received: " + str(received)
    #Cleanup - remove the external commitments added
    btc.update_commitments(external_to_remove=ecs)
Esempio n. 20
0
def test_failed_sendpayment(setup_podle, num_ygs, wallet_structures, mean_amt,
                            mixdepth, sending_amt):
    """Test of initiating joins, but failing to complete,
    to see commitment usage. YGs in background as per test_regtest.
    Use sweeps to avoid recover_from_nonrespondants without intruding
    into sendpayment code.
    """
    makercount = num_ygs
    answeryes = True
    txfee = 5000
    waittime = 3
    #Don't want to wait too long, but must account for possible
    #throttling with !auth
    jm_single().maker_timeout_sec = 12
    amount = 0
    wallets = make_wallets(makercount + 1,
                           wallet_structures=wallet_structures,
                           mean_amt=mean_amt)
    #the sendpayment bot uses the last wallet in the list
    wallet = wallets[makercount]['wallet']

    yigen_procs = []
    for i in range(makercount):
        ygp = local_command([python_cmd, yg_cmd,\
                             str(wallets[i]['seed'])], bg=True)
        time.sleep(2)  #give it a chance
        yigen_procs.append(ygp)

    #A significant delay is needed to wait for the yield generators to sync
    time.sleep(20)
    destaddr = btc.privkey_to_address(os.urandom(32),
                                      from_hex=False,
                                      magicbyte=get_p2pk_vbyte())

    addr_valid, errormsg = validate_address(destaddr)
    assert addr_valid, "Invalid destination address: " + destaddr + \
           ", error message: " + errormsg

    #TODO paramatetrize this as a test variable
    chooseOrdersFunc = weighted_order_choose

    log.debug('starting sendpayment')

    jm_single().bc_interface.sync_wallet(wallet)

    #Trigger PING LAG sending artificially
    joinmarket.irc.PING_INTERVAL = 3

    mcs = [IRCMessageChannel(c) for c in get_irc_mchannels()]
    mcc = MessageChannelCollection(mcs)

    #Allow taker more retries than makers allow, so as to trigger
    #blacklist failure case
    jm_single().config.set("POLICY", "taker_utxo_retries", "4")

    #override ioauth receipt with a dummy do-nothing callback:
    def on_ioauth(*args):
        log.debug("Taker received: " + ','.join([str(x) for x in args]))

    class DummySendPayment(sendpayment.SendPayment):
        def __init__(self, msgchan, wallet, destaddr, amount, makercount,
                     txfee, waittime, mixdepth, answeryes, chooseOrdersFunc,
                     on_ioauth):
            self.on_ioauth = on_ioauth
            self.podle_fails = 0
            self.podle_allowed_fails = 3  #arbitrary; but do it more than once
            self.retries = 0
            super(DummySendPayment,
                  self).__init__(msgchan, wallet, destaddr, amount, makercount,
                                 txfee, waittime, mixdepth, answeryes,
                                 chooseOrdersFunc)

        def on_welcome(self):
            Taker.on_welcome(self)
            DummyPaymentThread(self).start()

    class DummyPaymentThread(sendpayment.PaymentThread):
        def finishcallback(self, coinjointx):
            #Don't ignore makers and just re-start
            self.taker.retries += 1
            if self.taker.podle_fails == self.taker.podle_allowed_fails:
                self.taker.msgchan.shutdown()
                return
            self.create_tx()

        def create_tx(self):
            try:
                super(DummyPaymentThread, self).create_tx()
            except btc.PoDLEError:
                log.debug("Got one commit failure, continuing")
                self.taker.podle_fails += 1

    taker = DummySendPayment(mcc, wallet, destaddr, amount, makercount, txfee,
                             waittime, mixdepth, answeryes, chooseOrdersFunc,
                             on_ioauth)
    try:
        log.debug('starting message channels')
        mcc.run()
    finally:
        if any(yigen_procs):
            for ygp in yigen_procs:
                #NB *GENTLE* shutdown is essential for
                #test coverage reporting!
                ygp.send_signal(signal.SIGINT)
                ygp.wait()
    #We should have been able to try (tur -1) + podle_allowed_fails times
    assert taker.retries == jm_single().config.getint(
        "POLICY", "taker_utxo_retries") + taker.podle_allowed_fails
    #wait for block generation
    time.sleep(2)
    received = jm_single().bc_interface.get_received_by_addr(
        [destaddr], None)['data'][0]['balance']
    #Sanity check no transaction succeeded
    assert received == 0
Esempio n. 21
0
def test_sendpayment(setup_regtest, num_ygs, wallet_structures, mean_amt,
                     mixdepth, sending_amt, ygcfs, fails, donate):
    """Test of sendpayment code, with yield generators in background.
    """
    log = get_log()
    makercount = num_ygs
    answeryes = True
    txfee = 5000
    waittime = 5
    amount = sending_amt
    wallets = make_wallets(makercount + 1,
                           wallet_structures=wallet_structures,
                           mean_amt=mean_amt)
    #the sendpayment bot uses the last wallet in the list
    wallet = wallets[makercount]['wallet']

    yigen_procs = []
    if ygcfs:
        assert makercount == len(ygcfs)
    for i in range(makercount):
        if ygcfs:
            #back up default config, overwrite before start
            os.rename("joinmarket.cfg", "joinmarket.cfg.bak")
            shutil.copy2(ygcfs[i], "joinmarket.cfg")
        ygp = local_command([python_cmd, yg_cmd,\
                             str(wallets[i]['seed'])], bg=True)
        time.sleep(2)  #give it a chance
        yigen_procs.append(ygp)
        if ygcfs:
            #Note: in case of using multiple configs,
            #the starting config is what is used by sendpayment
            os.rename("joinmarket.cfg.bak", "joinmarket.cfg")

    #A significant delay is needed to wait for the yield generators to sync
    time.sleep(20)
    if donate:
        destaddr = None
    else:
        destaddr = btc.privkey_to_address(os.urandom(32),
                                          from_hex=False,
                                          magicbyte=get_p2pk_vbyte())
        addr_valid, errormsg = validate_address(destaddr)
        assert addr_valid, "Invalid destination address: " + destaddr + \
           ", error message: " + errormsg

    #TODO paramatetrize this as a test variable
    chooseOrdersFunc = weighted_order_choose

    log.debug('starting sendpayment')

    jm_single().bc_interface.sync_wallet(wallet)

    #Trigger PING LAG sending artificially
    joinmarket.irc.PING_INTERVAL = 3

    mcs = [IRCMessageChannel(c) for c in get_irc_mchannels()]
    mcc = MessageChannelCollection(mcs)
    #hack fix for #356 if multiple orders per counterparty
    #removed for now.
    #if amount==0: makercount=2
    taker = sendpayment.SendPayment(mcc, wallet, destaddr, amount,
                                    makercount - 2, txfee, waittime, mixdepth,
                                    answeryes, chooseOrdersFunc)
    try:
        log.debug('starting message channels')
        mcc.run(failures=fails)
    finally:
        if any(yigen_procs):
            for ygp in yigen_procs:
                #NB *GENTLE* shutdown is essential for
                #test coverage reporting!
                ygp.send_signal(signal.SIGINT)
                ygp.wait()
    #wait for block generation
    time.sleep(5)
    if not donate:
        received = jm_single().bc_interface.get_received_by_addr(
            [destaddr], None)['data'][0]['balance']
        if amount != 0:
            assert received == amount, "sendpayment failed - coins not arrived, " +\
               "received: " + str(received)
        #TODO: how to check success for sweep case?
        else:
            assert received != 0
Esempio n. 22
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())
Esempio n. 23
0
def main():
    parser = OptionParser(
        usage='usage: %prog [options] [auth utxo] [cjamount] [cjaddr] ['
        'changeaddr] [utxos..]',
        description=('Creates an unsigned coinjoin transaction. Outputs '
                     'a partially signed transaction hex string. The user '
                     'must sign their inputs independently and broadcast '
                     'them. The JoinMarket protocol requires the taker to '
                     'have a single p2pk UTXO input to use to '
                     'authenticate the  encrypted messages. For this '
                     'reason you must pass auth utxo and the '
                     'corresponding private key'))

    # for cjamount=0 do a sweep, and ignore change address
    parser.add_option('-f',
                      '--txfee',
                      action='store',
                      type='int',
                      dest='txfee',
                      default=10000,
                      help='total miner fee in satoshis, default=10000')
    parser.add_option(
        '-w',
        '--wait-time',
        action='store',
        type='float',
        dest='waittime',
        help='wait time in seconds to allow orders to arrive, default=5',
        default=5)
    parser.add_option('-N',
                      '--makercount',
                      action='store',
                      type='int',
                      dest='makercount',
                      help='how many makers to coinjoin with, default=2',
                      default=2)
    parser.add_option(
        '-C',
        '--choose-cheapest',
        action='store_true',
        dest='choosecheapest',
        default=False,
        help='override weightened offers picking and choose cheapest')
    parser.add_option(
        '-P',
        '--pick-orders',
        action='store_true',
        dest='pickorders',
        default=False,
        help='manually pick which orders to take. doesn\'t work while sweeping.'
    )
    parser.add_option('--yes',
                      action='store_true',
                      dest='answeryes',
                      default=False,
                      help='answer yes to everything')
    # TODO implement parser.add_option('-n', '--no-network',
    # action='store_true', dest='nonetwork', default=False, help='dont query
    # the blockchain interface, instead user must supply value of UTXOs on '
    # + ' command line in the format txid:output/value-in-satoshi')
    (options, args) = parser.parse_args()

    if len(args) < 3:
        parser.error('Needs a wallet, amount and destination address')
        sys.exit(0)
    auth_utxo = args[0]
    cjamount = int(args[1])
    destaddr = args[2]
    changeaddr = args[3]
    cold_utxos = args[4:]

    load_program_config()
    addr_valid1, errormsg1 = validate_address(destaddr)
    errormsg2 = None
    # if amount = 0 dont bother checking changeaddr so user can write any junk
    if cjamount != 0:
        addr_valid2, errormsg2 = validate_address(changeaddr)
    else:
        addr_valid2 = True
    if not addr_valid1 or not addr_valid2:
        if not addr_valid1:
            print 'ERROR: Address invalid. ' + errormsg1
        else:
            print 'ERROR: Address invalid. ' + errormsg2
        return

    all_utxos = [auth_utxo] + cold_utxos
    query_result = jm_single().bc_interface.query_utxo_set(all_utxos)
    if None in query_result:
        print query_result
    utxo_data = {}
    for utxo, data in zip(all_utxos, query_result):
        utxo_data[utxo] = {'address': data['address'], 'value': data['value']}
    auth_privkey = raw_input('input private key for ' +
                             utxo_data[auth_utxo]['address'] + ' :')
    if utxo_data[auth_utxo]['address'] != btc.privtoaddr(
            auth_privkey, magicbyte=get_p2pk_vbyte()):
        print 'ERROR: privkey does not match auth utxo'
        return

    if options.pickorders and cjamount != 0:  # cant use for sweeping
        chooseOrdersFunc = pick_order
    elif options.choosecheapest:
        chooseOrdersFunc = cheapest_order_choose
    else:  # choose randomly (weighted)
        chooseOrdersFunc = weighted_order_choose

    jm_single().nickname = random_nick()
    log.debug('starting sendpayment')

    class UnsignedTXWallet(AbstractWallet):
        def get_key_from_addr(self, addr):
            log.debug('getting privkey of ' + addr)
            if btc.privtoaddr(auth_privkey,
                              magicbyte=get_p2pk_vbyte()) != addr:
                raise RuntimeError('privkey doesnt match given address')
            return auth_privkey

    wallet = UnsignedTXWallet()
    irc = IRCMessageChannel(jm_single().nickname)
    taker = CreateUnsignedTx(irc, wallet, auth_utxo, cjamount, destaddr,
                             changeaddr, utxo_data, options, 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())
Esempio n. 24
0
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] [auth utxo] [cjamount] [cjaddr] ['
                  'changeaddr] [utxos..]',
            description=('Creates an unsigned coinjoin transaction. Outputs '
                         'a partially signed transaction hex string. The user '
                         'must sign their inputs independently and broadcast '
                         'them. The JoinMarket protocol requires the taker to '
                         'have a single p2pk UTXO input to use to '
                         'authenticate the  encrypted messages. For this '
                         'reason you must pass auth utxo and the '
                         'corresponding private key'))

    # for cjamount=0 do a sweep, and ignore change address
    parser.add_option(
            '-f',
            '--txfee',
            action='store',
            type='int',
            dest='txfee',
            default=10000,
            help='total miner fee in satoshis, default=10000')
    parser.add_option(
            '-w',
            '--wait-time',
            action='store',
            type='float',
            dest='waittime',
            help='wait time in seconds to allow orders to arrive, default=5',
            default=5)
    parser.add_option(
            '-N',
            '--makercount',
            action='store',
            type='int',
            dest='makercount',
            help='how many makers to coinjoin with, default=2',
            default=2)
    parser.add_option(
            '-C',
            '--choose-cheapest',
            action='store_true',
            dest='choosecheapest',
            default=False,
            help='override weightened offers picking and choose cheapest')
    parser.add_option(
            '-P',
            '--pick-orders',
            action='store_true',
            dest='pickorders',
            default=False,
            help=
            'manually pick which orders to take. doesn\'t work while sweeping.')
    parser.add_option(
            '--yes',
            action='store_true',
            dest='answeryes',
            default=False,
            help='answer yes to everything')
    # TODO implement parser.add_option('-n', '--no-network',
    # action='store_true', dest='nonetwork', default=False, help='dont query
    # the blockchain interface, instead user must supply value of UTXOs on '
    # + ' command line in the format txid:output/value-in-satoshi')
    (options, args) = parser.parse_args()

    if len(args) < 3:
        parser.error('Needs a wallet, amount and destination address')
        sys.exit(0)
    auth_utxo = args[0]
    cjamount = int(args[1])
    destaddr = args[2]
    changeaddr = args[3]
    cold_utxos = args[4:]

    load_program_config()
    addr_valid1, errormsg1 = validate_address(destaddr)
    errormsg2 = None
    # if amount = 0 dont bother checking changeaddr so user can write any junk
    if cjamount != 0:
        addr_valid2, errormsg2 = validate_address(changeaddr)
    else:
        addr_valid2 = True
    if not addr_valid1 or not addr_valid2:
        if not addr_valid1:
            print 'ERROR: Address invalid. ' + errormsg1
        else:
            print 'ERROR: Address invalid. ' + errormsg2
        return

    all_utxos = [auth_utxo] + cold_utxos
    query_result = jm_single().bc_interface.query_utxo_set(all_utxos)
    if None in query_result:
        print query_result
    utxo_data = {}
    for utxo, data in zip(all_utxos, query_result):
        utxo_data[utxo] = {'address': data['address'], 'value': data['value']}
    auth_privkey = raw_input('input private key for ' + utxo_data[auth_utxo][
        'address'] + ' :')
    if utxo_data[auth_utxo]['address'] != btc.privtoaddr(
            auth_privkey, get_p2pk_vbyte()):
        print 'ERROR: privkey does not match auth utxo'
        return

    if options.pickorders and cjamount != 0:  # cant use for sweeping
        chooseOrdersFunc = pick_order
    elif options.choosecheapest:
        chooseOrdersFunc = cheapest_order_choose
    else:  # choose randomly (weighted)
        chooseOrdersFunc = weighted_order_choose

    jm_single().nickname = random_nick()
    log.debug('starting sendpayment')

    class UnsignedTXWallet(AbstractWallet):

        def get_key_from_addr(self, addr):
            log.debug('getting privkey of ' + addr)
            if btc.privtoaddr(auth_privkey, get_p2pk_vbyte()) != addr:
                raise RuntimeError('privkey doesnt match given address')
            return auth_privkey

    wallet = UnsignedTXWallet()
    irc = IRCMessageChannel(jm_single().nickname)
    taker = CreateUnsignedTx(irc, wallet, auth_utxo, cjamount, destaddr,
                             changeaddr, utxo_data, options, 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())
Esempio n. 26
0
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())
Esempio n. 27
0
def test_tumbler(setup_tumbler, num_ygs, wallet_structures, mean_amt, sdev_amt,
                 yg_excess):
    """Test of tumbler code, with yield generators in background.
    """
    log = get_log()
    options = Options()
    options.mixdepthsrc = 0
    options.mixdepthcount = 4
    options.minmakercount = 2
    options.makercountrange = (num_ygs, 0)
    options.maxcjfee = (0.01, 10000)
    options.txfee = 5000
    options.addrcount = 3
    options.donateamount = 0.5
    options.txcountparams = (4, 1)
    options.mintxcount = 1
    options.amountpower = 100
    options.timelambda = 0.2
    options.waittime = 10
    options.mincjamount = 1000000
    options.liquiditywait = 5
    options.maxbroadcasts = 4
    options.maxcreatetx = 9
    options = vars(options)

    wallets = make_wallets(num_ygs + 1,
                           wallet_structures=wallet_structures,
                           mean_amt=mean_amt, sdev_amt=sdev_amt)
    #need to make sure that at least some ygs have substantially
    #more coins for last stages of sweep/spend in tumble:
    for i in range(num_ygs):
        jm_single().bc_interface.grab_coins(
                            wallets[i]['wallet'].get_external_addr(0), yg_excess)    
    #the tumbler bot uses the last wallet in the list
    wallet = wallets[num_ygs]['wallet']

    yigen_procs = []
    for i in range(num_ygs):
        ygp = local_command([python_cmd, yg_cmd,\
                             str(wallets[i]['seed'])], bg=True)
        time.sleep(2)  #give it a chance
        yigen_procs.append(ygp)

    #A significant delay is needed to wait for the yield generators to sync
    time.sleep(20)
    destaddrs = []
    for i in range(3):
        if btc.secp_present:
            destaddr = btc.privkey_to_address(
                os.urandom(32),
                from_hex=False,
                magicbyte=get_p2pk_vbyte())
        else:
            destaddr = btc.privkey_to_address(
                os.urandom(32),
                magicbyte=get_p2pk_vbyte())
        addr_valid, errormsg = validate_address(destaddr)
        assert addr_valid, "Invalid destination address: " + destaddr + \
                   ", error message: " + errormsg
        destaddrs.append(destaddr)
    tx_list = tumbler.generate_tumbler_tx(destaddrs, options)
    pprint(tx_list)
    if options['addrcount'] + 1 > options['mixdepthcount']:
        print('not enough mixing depths to pay to all destination addresses, '
              'increasing mixdepthcount')
        options['mixdepthcount'] = options['addrcount'] + 1

    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')

    jm_single().nickname = random_nick()

    log.debug('starting tumbler')

    jm_single().bc_interface.sync_wallet(wallet)
    jm_single().bc_interface.pushtx_failure_prob = 0.4
    irc = IRCMessageChannel(jm_single().nickname)
    tumbler_bot = tumbler.Tumbler(irc, wallet, tx_list, options)
    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(tumbler_bot)
        import traceback
        log.debug(traceback.format_exc())
    finally:
        if any(yigen_procs):
            for ygp in yigen_procs:
                #NB *GENTLE* shutdown is essential for
                #test coverage reporting!
                ygp.send_signal(signal.SIGINT)
                ygp.wait()
    #wait for block generation
    time.sleep(5)
    received = jm_single().bc_interface.get_received_by_addr(
        [destaddr], None)['data'][0]['balance']
    assert received != 0
    """TODO: figure out a sensible assertion check for the destination
Esempio n. 28
0
def test_tx_commitments_used(setup_podle, consume_tx, age_required, cmt_age):
    tries = jm_single().config.getint("POLICY", "taker_utxo_retries")
    #remember and reset at the end
    taker_utxo_age = jm_single().config.getint("POLICY", "taker_utxo_age")
    jm_single().config.set("POLICY", "taker_utxo_age", str(age_required))
    #Don't want to wait too long, but must account for possible
    #throttling with !auth
    jm_single().maker_timeout_sec = 12
    amount = 0
    wallets = make_wallets(3,
                           wallet_structures=[[1, 2, 1, 0, 0], [1, 2, 0, 0, 0],
                                              [2, 2, 1, 0, 0]],
                           mean_amt=1)
    #the sendpayment bot uses the last wallet in the list
    wallet = wallets[2]['wallet']

    #make_wallets calls grab_coins which mines 1 block per individual payout,
    #so the age of the coins depends on where they are in that list. The sendpayment
    #is the last wallet in the list, and we choose the non-tx utxos which are in
    #mixdepth 1 and 2 (2 and 1 utxos in each respectively). We filter for those
    #that have sufficient age, so to get 1 which is old enough, it will be the oldest,
    #which will have an age of 2 + 1 (the first utxo spent to that wallet).
    #So if we need an age of 6, we need to mine 3 more blocks.
    blocks_reqd = cmt_age - 3
    jm_single().bc_interface.tick_forward_chain(blocks_reqd)
    yigen_procs = []
    for i in range(2):
        ygp = local_command([python_cmd, yg_cmd,\
                             str(wallets[i]['seed'])], bg=True)
        time.sleep(2)  #give it a chance
        yigen_procs.append(ygp)

    time.sleep(5)
    destaddr = btc.privkey_to_address(binascii.hexlify(os.urandom(32)),
                                      magicbyte=get_p2pk_vbyte())
    addr_valid, errormsg = validate_address(destaddr)
    assert addr_valid, "Invalid destination address: " + destaddr + \
           ", error message: " + errormsg

    log.debug('starting sendpayment')

    jm_single().bc_interface.sync_wallet(wallet)
    log.debug("Here is the whole wallet: \n" + str(wallet.unspent))
    #Trigger PING LAG sending artificially
    joinmarket.irc.PING_INTERVAL = 3

    mcs = [IRCMessageChannel(c) for c in get_irc_mchannels()]
    mcc = MessageChannelCollection(mcs)
    if consume_tx:
        #add all utxo in mixdepth 0 to 'used' list of commitments,
        utxos = wallet.get_utxos_by_mixdepth()[0]
        for u, addrval in utxos.iteritems():
            priv = wallet.get_key_from_addr(addrval['address'])
            podle = btc.PoDLE(u, priv)
            for i in range(tries):
                #loop because we want to use up all retries of this utxo
                commitment = podle.generate_podle(i)['commit']
                btc.update_commitments(commitment=commitment)

    #Now test a sendpayment from mixdepth 0 with all the depth 0 utxos
    #used up, so that the other utxos in the wallet get used.
    taker = sendpayment.SendPayment(mcc, wallet, destaddr, amount, 2, 5000, 3,
                                    0, True, weighted_order_choose)
    try:
        log.debug('starting message channels')
        mcc.run()
    finally:
        if any(yigen_procs):
            for ygp in yigen_procs:
                #NB *GENTLE* shutdown is essential for
                #test coverage reporting!
                ygp.send_signal(signal.SIGINT)
                ygp.wait()
    #wait for block generation
    time.sleep(5)
    received = jm_single().bc_interface.get_received_by_addr(
        [destaddr], None)['data'][0]['balance']
    jm_single().config.set("POLICY", "taker_utxo_age", str(taker_utxo_age))
    if cmt_age < age_required:
        assert received == 0, "Coins arrived but shouldn't"
    else:
        assert received != 0, "sendpayment failed - coins not arrived, " +\
           "received: " + str(received)
Esempio n. 29
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()
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()
Esempio n. 31
0
def main():
    parser = OptionParser(
        usage=
        'usage: %prog [options] [cjamount] [cjaddr] [changeaddr] [utxos..]',
        description=('Creates an unsigned coinjoin transaction. Outputs '
                     'a partially signed transaction hex string. The user '
                     'must sign their inputs independently and broadcast '
                     'them. The JoinMarket protocol requires the taker to '
                     'have a single p2pk UTXO input to use to '
                     'authenticate the  encrypted messages. For this '
                     'reason you must pass auth utxo and the '
                     'corresponding private key'))

    # for cjamount=0 do a sweep, and ignore change address
    parser.add_option('-f',
                      '--txfee',
                      action='store',
                      type='int',
                      dest='txfee',
                      default=10000,
                      help='total miner fee in satoshis, default=10000')
    parser.add_option(
        '-w',
        '--wait-time',
        action='store',
        type='float',
        dest='waittime',
        help='wait time in seconds to allow orders to arrive, default=5',
        default=5)
    parser.add_option('-N',
                      '--makercount',
                      action='store',
                      type='int',
                      dest='makercount',
                      help='how many makers to coinjoin with, default=2',
                      default=2)
    parser.add_option(
        '-C',
        '--choose-cheapest',
        action='store_true',
        dest='choosecheapest',
        default=False,
        help='override weightened offers picking and choose cheapest')
    parser.add_option(
        '-P',
        '--pick-orders',
        action='store_true',
        dest='pickorders',
        default=False,
        help='manually pick which orders to take. doesn\'t work while sweeping.'
    )
    parser.add_option('--yes',
                      action='store_true',
                      dest='answeryes',
                      default=False,
                      help='answer yes to everything')
    # TODO implement parser.add_option('-n', '--no-network',
    # action='store_true', dest='nonetwork', default=False, help='dont query
    # the blockchain interface, instead user must supply value of UTXOs on '
    # + ' command line in the format txid:output/value-in-satoshi')
    (options, args) = parser.parse_args()

    if len(args) < 4:
        parser.error(
            'Needs an amount, destination address, change address and utxos ')
        sys.exit(0)
    cjamount = int(args[0])
    destaddr = args[1]
    changeaddr = args[2]
    cold_utxos = args[3:]

    load_program_config()
    addr_valid1, errormsg1 = validate_address(destaddr)
    errormsg2 = None
    # if amount = 0 dont bother checking changeaddr so user can write any junk
    if cjamount != 0:
        addr_valid2, errormsg2 = validate_address(changeaddr)
    else:
        addr_valid2 = True
    if not addr_valid1 or not addr_valid2:
        if not addr_valid1:
            print 'ERROR: Address invalid. ' + errormsg1
        else:
            print 'ERROR: Address invalid. ' + errormsg2
        return

    query_result = jm_single().bc_interface.query_utxo_set(cold_utxos)
    if None in query_result:
        print query_result
    utxo_data = {}
    for utxo, data in zip(cold_utxos, query_result):
        utxo_data[utxo] = {'address': data['address'], 'value': data['value']}
    print("Got this utxo data: " + str(utxo_data))
    if options.pickorders and cjamount != 0:  # cant use for sweeping
        chooseOrdersFunc = pick_order
    elif options.choosecheapest:
        chooseOrdersFunc = cheapest_order_choose
    else:  # choose randomly (weighted)
        chooseOrdersFunc = weighted_order_choose

    wallet = AbstractWallet()
    mcs = [IRCMessageChannel(c) for c in get_irc_mchannels()]
    mcc = MessageChannelCollection(mcs)
    taker = CreateUnsignedTx(mcc, wallet, cjamount, destaddr, changeaddr,
                             utxo_data, options, chooseOrdersFunc)
    log.debug('starting create-unsigned-tx')
    try:
        log.info('starting message channels')
        mcc.run()
    except:
        log.warn('CRASHING, DUMPING EVERYTHING')
        debug_dump_object(wallet,
                          ['addr_cache', 'keys', 'wallet_name', 'seed'])
        debug_dump_object(taker)
        import traceback
        log.debug(traceback.format_exc())