Example #1
0
def test_simple_coinjoin(monkeypatch, tmpdir, setup_cj, wallet_cls):
    def raise_exit(i):
        raise Exception("sys.exit called")
    monkeypatch.setattr(sys, 'exit', raise_exit)
    set_commitment_file(str(tmpdir.join('commitments.json')))

    MAKER_NUM = 3
    wallets = make_wallets_to_list(make_wallets(
        MAKER_NUM + 1, wallet_structures=[[4, 0, 0, 0, 0]] * (MAKER_NUM + 1),
        mean_amt=1, wallet_cls=wallet_cls))

    jm_single().bc_interface.tickchain()
    sync_wallets(wallets)

    makers = [YieldGeneratorBasic(
        wallets[i],
        [0, 2000, 0, 'swabsoffer', 10**7]) for i in range(MAKER_NUM)]

    orderbook = create_orderbook(makers)
    assert len(orderbook) == MAKER_NUM

    cj_amount = int(1.1 * 10**8)
    # mixdepth, amount, counterparties, dest_addr, waittime
    schedule = [(0, cj_amount, MAKER_NUM, 'INTERNAL', 0)]
    taker = create_taker(wallets[-1], schedule, monkeypatch)

    active_orders, maker_data = init_coinjoin(taker, makers,
                                              orderbook, cj_amount)

    txdata = taker.receive_utxos(maker_data)
    assert txdata[0], "taker.receive_utxos error"

    taker_final_result = do_tx_signing(taker, makers, active_orders, txdata)
    assert taker_final_result is not False
    assert taker.on_finished_callback.status is not False
def test_coinjoin_mixdepth_wrap_taker(monkeypatch, tmpdir, setup_cj):
    def raise_exit(i):
        raise Exception("sys.exit called")

    monkeypatch.setattr(sys, 'exit', raise_exit)
    set_commitment_file(str(tmpdir.join('commitments.json')))

    MAKER_NUM = 3
    wallet_services = make_wallets_to_list(
        make_wallets(MAKER_NUM + 1,
                     wallet_structures=[[4, 0, 0, 0, 0]] * MAKER_NUM +
                     [[0, 0, 0, 0, 3]],
                     mean_amt=1))

    for wallet_service in wallet_services:
        assert wallet_service.max_mixdepth == 4

    jm_single().bc_interface.tickchain()
    jm_single().bc_interface.tickchain()

    sync_wallets(wallet_services)

    cj_fee = 2000
    makers = [
        YieldGeneratorBasic(
            wallet_services[i],
            [0, cj_fee, 0, absoffer_type_map[SegwitWallet], 10**7])
        for i in range(MAKER_NUM)
    ]
    create_orders(makers)

    orderbook = create_orderbook(makers)
    assert len(orderbook) == MAKER_NUM

    cj_amount = int(1.1 * 10**8)
    # mixdepth, amount, counterparties, dest_addr, waittime, rounding
    schedule = [(4, cj_amount, MAKER_NUM, 'INTERNAL', 0, NO_ROUNDING)]
    taker = create_taker(wallet_services[-1], schedule, monkeypatch)

    active_orders, maker_data = init_coinjoin(taker, makers, orderbook,
                                              cj_amount)

    txdata = taker.receive_utxos(maker_data)
    assert txdata[0], "taker.receive_utxos error"

    taker_final_result = do_tx_signing(taker, makers, active_orders, txdata)
    assert taker_final_result is not False

    tx = btc.CMutableTransaction.deserialize(hextobin(txdata[2]))

    wallet_service = wallet_services[-1]
    # TODO change for new tx monitoring:
    wallet_service.remove_old_utxos(tx)
    wallet_service.add_new_utxos(tx)

    balances = wallet_service.get_balance_by_mixdepth()
    assert balances[0] == cj_amount
    # <= because of tx fee
    assert balances[4] <= 3 * 10**8 - cj_amount - (cj_fee * MAKER_NUM)
def test_coinjoin_mixdepth_wrap_maker(monkeypatch, tmpdir, setup_cj):
    def raise_exit(i):
        raise Exception("sys.exit called")

    monkeypatch.setattr(sys, 'exit', raise_exit)
    set_commitment_file(str(tmpdir.join('commitments.json')))

    MAKER_NUM = 2
    wallet_services = make_wallets_to_list(
        make_wallets(MAKER_NUM + 1,
                     wallet_structures=[[0, 0, 0, 0, 4]] * MAKER_NUM +
                     [[3, 0, 0, 0, 0]],
                     mean_amt=1))

    for wallet_service in wallet_services:
        assert wallet_service.max_mixdepth == 4

    jm_single().bc_interface.tickchain()
    jm_single().bc_interface.tickchain()

    sync_wallets(wallet_services)

    cj_fee = 2000
    makers = [
        YieldGeneratorBasic(wallet_services[i],
                            [0, cj_fee, 0, 'swabsoffer', 10**7])
        for i in range(MAKER_NUM)
    ]

    orderbook = create_orderbook(makers)
    assert len(orderbook) == MAKER_NUM

    cj_amount = int(1.1 * 10**8)
    # mixdepth, amount, counterparties, dest_addr, waittime, rounding
    schedule = [(0, cj_amount, MAKER_NUM, 'INTERNAL', 0, NO_ROUNDING)]
    taker = create_taker(wallet_services[-1], schedule, monkeypatch)

    active_orders, maker_data = init_coinjoin(taker, makers, orderbook,
                                              cj_amount)

    txdata = taker.receive_utxos(maker_data)
    assert txdata[0], "taker.receive_utxos error"

    taker_final_result = do_tx_signing(taker, makers, active_orders, txdata)
    assert taker_final_result is not False

    tx = btc.deserialize(txdata[2])
    binarize_tx(tx)

    for i in range(MAKER_NUM):
        wallet_service = wallet_services[i]
        # TODO as above re: monitoring
        wallet_service.remove_old_utxos_(tx)
        wallet_service.add_new_utxos_(tx, b'\x00' * 32)  # fake txid

        balances = wallet_service.get_balance_by_mixdepth()
        assert balances[0] == cj_amount
        assert balances[4] == 4 * 10**8 - cj_amount + cj_fee
def load_program_config(config_path=None, bs=None):
    global_singleton.config.readfp(io.BytesIO(defaultconfig))
    if not config_path:
        config_path = os.getcwd()
    global_singleton.config_location = os.path.join(
        config_path, global_singleton.config_location)
    loadedFiles = global_singleton.config.read(
        [global_singleton.config_location])
    #Hack required for electrum; must be able to enforce a different
    #blockchain interface even in default/new load.
    if bs:
        global_singleton.config.set("BLOCKCHAIN", "blockchain_source", bs)
    # Create default config file if not found
    if len(loadedFiles) != 1:
        with open(global_singleton.config_location, "w") as configfile:
            configfile.write(defaultconfig)

    # check for sections
    #These are left as sanity checks but currently impossible
    #since any edits are overlays to the default, these sections/options will
    #always exist.
    for s in required_options:  #pragma: no cover
        if s not in global_singleton.config.sections():
            raise Exception(
                "Config file does not contain the required section: " + s)
    # then check for specific options
    for k, v in required_options.iteritems():  #pragma: no cover
        for o in v:
            if o not in global_singleton.config.options(k):
                raise Exception(
                    "Config file does not contain the required option: " + o)

    loglevel = global_singleton.config.get("LOGGING", "console_log_level")
    try:
        set_logging_level(loglevel)
    except:
        print(
            "Failed to set logging level, must be DEBUG, INFO, WARNING, ERROR")
    try:
        global_singleton.maker_timeout_sec = global_singleton.config.getint(
            'TIMEOUT', 'maker_timeout_sec')
    except NoOptionError:  #pragma: no cover
        log.debug('TIMEOUT/maker_timeout_sec not found in .cfg file, '
                  'using default value')

    # configure the interface to the blockchain on startup
    global_singleton.bc_interface = get_blockchain_interface_instance(
        global_singleton.config)

    #set the location of the commitments file
    try:
        global_singleton.commit_file_location = global_singleton.config.get(
            "POLICY", "commit_file_location")
    except NoOptionError:  #pragma: no cover
        log.debug("No commitment file location in config, using default "
                  "location cmtdata/commitments.json")
    set_commitment_file(
        os.path.join(config_path, global_singleton.commit_file_location))
Example #5
0
def test_coinjoin_mixed_maker_addresses(monkeypatch, tmpdir, setup_cj,
                                        wallet_cls, wallet_cls_sec):
    set_commitment_file(str(tmpdir.join('commitments.json')))

    MAKER_NUM = 2
    wallet_services = make_wallets_to_list(
        make_wallets(MAKER_NUM + 1,
                     wallet_structures=[[1, 0, 0, 0, 0]] * MAKER_NUM +
                     [[3, 0, 0, 0, 0]],
                     mean_amt=1,
                     wallet_cls=wallet_cls))
    wallet_services_sec = make_wallets_to_list(
        make_wallets(MAKER_NUM,
                     wallet_structures=[[1, 0, 0, 0, 0]] * MAKER_NUM,
                     mean_amt=1,
                     wallet_cls=wallet_cls_sec))

    for i in range(MAKER_NUM):
        wif = wallet_services_sec[i].get_wif(0, False, 0)
        wallet_services[i].wallet.import_private_key(
            0, wif, key_type=wallet_services_sec[i].wallet.TYPE)

    jm_single().bc_interface.tickchain()
    jm_single().bc_interface.tickchain()

    sync_wallets(wallet_services, fast=False)

    makers = [
        YieldGeneratorBasic(wallet_services[i],
                            [0, 2000, 0, 'swabsoffer', 10**7])
        for i in range(MAKER_NUM)
    ]

    orderbook = create_orderbook(makers)

    cj_amount = int(1.1 * 10**8)
    # mixdepth, amount, counterparties, dest_addr, waittime, rounding
    schedule = [(0, cj_amount, MAKER_NUM, 'INTERNAL', 0, NO_ROUNDING)]
    taker = create_taker(wallet_services[-1], schedule, monkeypatch)

    active_orders, maker_data = init_coinjoin(taker, makers, orderbook,
                                              cj_amount)

    txdata = taker.receive_utxos(maker_data)
    assert txdata[0], "taker.receive_utxos error"

    taker_final_result = do_tx_signing(taker, makers, active_orders, txdata)
    assert taker_final_result is not False
    assert taker.on_finished_callback.status is not False
Example #6
0
def load_program_config(config_path=None, bs=None):
    global_singleton.config.readfp(io.StringIO(defaultconfig))
    remove_unwanted_default_settings(global_singleton.config)
    if not config_path:
        config_path = os.getcwd()
    global_singleton.config_location = os.path.join(
        config_path, global_singleton.config_location)
    loadedFiles = global_singleton.config.read(
        [global_singleton.config_location])
    #Hack required for electrum; must be able to enforce a different
    #blockchain interface even in default/new load.
    if bs:
        global_singleton.config.set("BLOCKCHAIN", "blockchain_source", bs)
    # Create default config file if not found
    if len(loadedFiles) != 1:
        with open(global_singleton.config_location, "w") as configfile:
            configfile.write(defaultconfig)
        jmprint(
            "Created a new `joinmarket.cfg`. Please review and adopt the "
            "settings and restart joinmarket.", "info")
        exit(1)

    #These are left as sanity checks but currently impossible
    #since any edits are overlays to the default, these sections/options will
    #always exist.
    # FIXME: This check is a best-effort attempt. Certain incorrect section
    # names can pass and so can non-first invalid sections.
    for s in required_options:  #pragma: no cover
        # check for sections
        avail = None
        if not global_singleton.config.has_section(s):
            for avail in global_singleton.config.sections():
                if avail.startswith(s):
                    break
            else:
                raise Exception(
                    "Config file does not contain the required section: " + s)
        # then check for specific options
        k = avail or s
        for o in required_options[s]:
            if not global_singleton.config.has_option(k, o):
                raise Exception("Config file does not contain the required "
                                "option '{}' in section '{}'.".format(o, k))

    loglevel = global_singleton.config.get("LOGGING", "console_log_level")
    try:
        set_logging_level(loglevel)
    except:
        jmprint(
            "Failed to set logging level, must be DEBUG, INFO, WARNING, ERROR",
            "error")

    # Logs to the console are color-coded if user chooses (file is unaffected)
    if global_singleton.config.get("LOGGING", "color") == "true":
        set_logging_color(True)
    else:
        set_logging_color(False)

    try:
        global_singleton.maker_timeout_sec = global_singleton.config.getint(
            'TIMEOUT', 'maker_timeout_sec')
    except NoOptionError:  #pragma: no cover
        log.debug('TIMEOUT/maker_timeout_sec not found in .cfg file, '
                  'using default value')

    # configure the interface to the blockchain on startup
    global_singleton.bc_interface = get_blockchain_interface_instance(
        global_singleton.config)

    #set the location of the commitments file
    try:
        global_singleton.commit_file_location = global_singleton.config.get(
            "POLICY", "commit_file_location")
    except NoOptionError:  #pragma: no cover
        log.debug("No commitment file location in config, using default "
                  "location cmtdata/commitments.json")
    set_commitment_file(
        os.path.join(config_path, global_singleton.commit_file_location))
Example #7
0
def load_program_config(config_path="", bs=None, plugin_services=[]):
    global_singleton.config.readfp(io.StringIO(defaultconfig))
    if not config_path:
        config_path = lookup_appdata_folder(global_singleton.APPNAME)
    # we set the global home directory, but keep the config_path variable
    # for callers of this function:
    global_singleton.datadir = config_path
    jmprint("User data location: " + global_singleton.datadir, "info")
    if not os.path.exists(global_singleton.datadir):
        os.makedirs(global_singleton.datadir)
    # prepare folders for wallets and logs
    if not os.path.exists(os.path.join(global_singleton.datadir, "wallets")):
        os.makedirs(os.path.join(global_singleton.datadir, "wallets"))
    if not os.path.exists(os.path.join(global_singleton.datadir, "logs")):
        os.makedirs(os.path.join(global_singleton.datadir, "logs"))
    if not os.path.exists(os.path.join(global_singleton.datadir, "cmtdata")):
        os.makedirs(os.path.join(global_singleton.datadir, "cmtdata"))
    global_singleton.config_location = os.path.join(
        global_singleton.datadir, global_singleton.config_location)

    remove_unwanted_default_settings(global_singleton.config)
    loadedFiles = global_singleton.config.read([global_singleton.config_location
                                               ])
    #Hack required for electrum; must be able to enforce a different
    #blockchain interface even in default/new load.
    if bs:
        global_singleton.config.set("BLOCKCHAIN", "blockchain_source", bs)
    # Create default config file if not found
    if len(loadedFiles) != 1:
        with open(global_singleton.config_location, "w") as configfile:
            configfile.write(defaultconfig)
        jmprint("Created a new `joinmarket.cfg`. Please review and adopt the "
              "settings and restart joinmarket.", "info")
        sys.exit(EXIT_FAILURE)

    #These are left as sanity checks but currently impossible
    #since any edits are overlays to the default, these sections/options will
    #always exist.
    # FIXME: This check is a best-effort attempt. Certain incorrect section
    # names can pass and so can non-first invalid sections.
    for s in required_options: #pragma: no cover
        # check for sections
        avail = None
        if not global_singleton.config.has_section(s):
            for avail in global_singleton.config.sections():
                if avail.startswith(s):
                    break
            else:
                raise Exception(
                    "Config file does not contain the required section: " + s)
        # then check for specific options
        k = avail or s
        for o in required_options[s]:
            if not global_singleton.config.has_option(k, o):
                raise Exception("Config file does not contain the required "
                                "option '{}' in section '{}'.".format(o, k))

    loglevel = global_singleton.config.get("LOGGING", "console_log_level")
    try:
        set_logging_level(loglevel)
    except:
        jmprint("Failed to set logging level, must be DEBUG, INFO, WARNING, ERROR",
                "error")

    # Logs to the console are color-coded if user chooses (file is unaffected)
    if global_singleton.config.get("LOGGING", "color") == "true":
        set_logging_color(True)
    else:
        set_logging_color(False)

    try:
        global_singleton.maker_timeout_sec = global_singleton.config.getint(
            'TIMEOUT', 'maker_timeout_sec')
    except NoOptionError: #pragma: no cover
        log.debug('TIMEOUT/maker_timeout_sec not found in .cfg file, '
                  'using default value')

    # configure the interface to the blockchain on startup
    global_singleton.bc_interface = get_blockchain_interface_instance(
        global_singleton.config)

    # set the location of the commitments file; for non-mainnet a different
    # file is used to avoid conflict
    try:
        global_singleton.commit_file_location = global_singleton.config.get(
            "POLICY", "commit_file_location")
    except NoOptionError: #pragma: no cover
        if get_network() == "mainnet":
            log.debug("No commitment file location in config, using default "
                  "location cmtdata/commitments.json")
    if get_network() != "mainnet":
        # no need to be flexible for tests; note this is used
        # for regtest, signet and testnet3
        global_singleton.commit_file_location = "cmtdata/" + get_network() + \
            "_commitments.json"
    set_commitment_file(os.path.join(config_path,
                                         global_singleton.commit_file_location))

    for p in plugin_services:
        # for now, at this config level, the only significance
        # of a "plugin" is that it keeps its own separate log.
        # We require that a section exists in the config file,
        # and that it has enabled=true:
        assert isinstance(p, JMPluginService)
        if not (global_singleton.config.has_section(p.name) and \
                global_singleton.config.has_option(p.name, "enabled") and \
                global_singleton.config.get(p.name, "enabled") == "true"):
            break
        if p.requires_logging:
            # make sure the environment can accept a logfile by
            # creating the directory in the correct place,
            # and setting that in the plugin object; the plugin
            # itself will switch on its own logging when ready,
            # attaching a filehandler to the global log.
            plogsdir = os.path.join(os.path.dirname(
                global_singleton.config_location), "logs", p.name)
            if not os.path.exists(plogsdir):
                os.makedirs(plogsdir)
            p.set_log_dir(plogsdir)