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