def coin(request): network = request.param Net.set_to(network) try: yield network.COIN finally: Net.set_to(SVMainnet)
def test_legacy_wallet_loading(storage_info: WalletStorageInfo) -> None: # When a wallet is composed of multiple files, we need to know which to load. wallet_filenames = [] if storage_info.kind != StorageKind.DATABASE: wallet_filenames.append(storage_info.filename) if storage_info.kind in (StorageKind.DATABASE, StorageKind.HYBRID): wallet_filenames.append(storage_info.filename + DATABASE_EXT) temp_dir = tempfile.mkdtemp() for _wallet_filename in wallet_filenames: source_wallet_path = os.path.join(TEST_WALLET_PATH, _wallet_filename) wallet_path = os.path.join(temp_dir, _wallet_filename) shutil.copyfile(source_wallet_path, wallet_path) wallet_filename = storage_info.filename wallet_path = os.path.join(temp_dir, wallet_filename) if "testnet" in wallet_filename: Net.set_to(SVTestnet) password = "******" storage = WalletStorage(wallet_path) if "passworded" in wallet_filename: storage.decrypt(password) try: parent_wallet = ParentWallet(storage) except OSError as e: if "is not a valid Win32 application" not in e.args[1]: raise e pytest.xfail("Missing libusb for this architecture") return if "standard" in wallet_filename: is_bip39 = "bip39" in wallet_filename check_legacy_parent_of_standard_wallet(parent_wallet, is_bip39=is_bip39, password=password) elif "imported_privkey" in wallet_filename: check_legacy_parent_of_imported_privkey_wallet(parent_wallet) elif "imported_address" in wallet_filename: check_legacy_parent_of_imported_address_wallet(parent_wallet) elif "multisig" in wallet_filename: check_legacy_parent_of_multisig_wallet(parent_wallet) elif "hardware" in wallet_filename: check_legacy_parent_of_hardware_wallet(parent_wallet) else: raise Exception(f"unrecognised wallet file {wallet_filename}") if "testnet" in wallet_filename: Net.set_to(SVMainnet)
def test_concurrent_tx_creation_and_broadcast(self, event_loop): n_txs = 10 Net.set_to(SVRegTestnet) p2pkh_object = SVRegTestnet.REGTEST_FUNDS_PUBLIC_KEY.to_address() P2PKH_OUTPUT = { "value": 10000, "script_pubkey": p2pkh_object.to_script().to_hex() } payload = { "split_value": 20000, "split_count": 100, "password": "******" } url = f'http://127.0.0.1:9999/v1/regtest/dapp/wallets/' \ f'{self.TEST_WALLET_NAME}/1/txs/split_utxos' # 1) split utxos sufficient for n transactions + confirm result = requests.post(url, json=payload) if result.status_code != 200: raise requests.exceptions.HTTPError(result.text) result2 = requests.post( f'http://127.0.0.1:9999/v1/regtest/dapp/wallets/' f'{self.TEST_WALLET_NAME}/1/generate_blocks') if result2.status_code != 200: raise requests.exceptions.HTTPError(result2.text) time.sleep(10) # 2) test concurrent transaction creation + broadcast payload2 = {"outputs": [P2PKH_OUTPUT], "password": "******"} async def main(): async with aiohttp.ClientSession() as session: tasks = [ asyncio.create_task(self.create_and_send( session, payload2)) for _ in range(0, n_txs) ] results = await asyncio.gather(*tasks, return_exceptions=True) for result in results: error_code = result.get('code') if error_code: assert False, str(Fault(error_code, result.get('message'))) event_loop.run_until_complete(main())
def test_parse_uri_bip276() -> None: Net.set_to(SVTestnet) try: d = parse_URI(BIP276_URI) finally: Net.set_to(SVMainnet) expected_message = "the money i owe you" assert d == { "bip276": BIP276_TEXT, "script": BIP276_DATA, "amount": 12112121, "message": expected_message, "memo": expected_message, }
def test_bip32_root(): Net.set_to(SVMainnet) k = BIP32_KeyStore({}) k.add_xprv_from_seed(b'BitcoinSV', 'm') assert k.xprv == ('xprv9s21ZrQH143K48ebsYkLU9UPzgdDVfhT6SMdWFJ8ZXak1bjKVRLu' 'xdmMCh7HZkwciZd7fga4gK4XW2QZhvWz5os6hJwqLfpZmW9r7pLgn9s') Net.set_to(SVTestnet) k = BIP32_KeyStore({}) k.add_xprv_from_seed(b'BitcoinSV', 'm') assert k.xprv == ('tprv8ZgxMBicQKsPewt8Y7bqdo6PJp3RjBjTRzGkNfib3W5DoCUQUng' 'fUP8o7sGwa8Kw619tfnBpqfeKxsxJq8rvts8hDxA912YcgbuGZX3AZDd') Net.set_to(SVMainnet)
def network(request): network = request.param Net.set_to(network) yield network Net.set_to(SVMainnet)
def main(): # The hook will only be used in the Qt GUI right now setup_thread_excepthook() # on osx, delete Process Serial Number arg generated for apps launched in Finder sys.argv = [x for x in sys.argv if not x.startswith('-psn')] # old 'help' syntax if len(sys.argv) > 1 and sys.argv[1] == 'help': sys.argv.remove('help') sys.argv.append('-h') # read arguments from stdin pipe and prompt for i, arg in enumerate(sys.argv): if arg == '-': if not sys.stdin.isatty(): sys.argv[i] = sys.stdin.read() break else: raise Exception('Cannot get argument from stdin') elif arg == '?': sys.argv[i] = input("Enter argument:") elif arg == ':': sys.argv[i] = prompt_password('Enter argument (will not echo):', False) # parse command line parser = get_parser() args = parser.parse_args() # config is an object passed to the various constructors (wallet, interface, gui) config_options = args.__dict__ config_options = { key: value for key, value in config_options.items() if value is not None and key not in config_variables.get(args.cmd, {}) } logs.set_level(config_options['verbose']) if config_options.get('server'): config_options['auto_connect'] = False config_options['cwd'] = os.getcwd() # fixme: this can probably be achieved with a runtime hook (pyinstaller) try: if is_bundle and os.path.exists( os.path.join(sys._MEIPASS, 'is_portable')): config_options['portable'] = True except AttributeError: config_options['portable'] = False if config_options.get('portable'): config_options['electrum_sv_path'] = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'electrum_sv_data') if config_options.get('file_logging'): log_path = os.path.join(platform.user_dir(prefer_local=True), "logs") os.makedirs(log_path, exist_ok=True) log_path = os.path.join(log_path, time.strftime("%Y%m%d-%H%M%S") + ".log") logs.add_file_output(log_path) if config_options.get('testnet'): Net.set_to(SVTestnet) # check uri uri = config_options.get('url') if uri: if not web.is_URI(uri): print('unknown command:', uri, file=sys.stderr) sys.exit(1) config_options['url'] = uri # todo: defer this to gui config = SimpleConfig(config_options) cmdname = config.get('cmd') # Set the app state proxy if cmdname == 'gui': try: from electrumsv.gui.qt.app_state import QtAppStateProxy except ImportError as e: platform.missing_import(e) QtAppStateProxy(config, 'qt') else: AppStateProxy(config, 'cmdline') # run non-RPC commands separately if cmdname in ['create', 'restore']: run_non_RPC(config) sys.exit(0) if cmdname == 'gui': fd, server = daemon.get_fd_or_server(config) if fd is not None: d = daemon.Daemon(fd, True) d.start() app_state.app.run_gui() sys.exit(0) else: result = server.gui(config_options) elif cmdname == 'daemon': subcommand = config.get('subcommand') if subcommand in ['load_wallet']: init_daemon(config_options) if subcommand in [None, 'start']: fd, server = daemon.get_fd_or_server(config) if fd is not None: if subcommand == 'start': pid = os.fork() if pid: print("starting daemon (PID %d)" % pid, file=sys.stderr) sys.exit(0) d = daemon.Daemon(fd, False) d.start() if config.get('websocket_server'): try: from electrumsv import websockets except ImportError as e: platform.missing_import(e) websockets.WebSocketServer(config, d.network).start() if config.get('requests_dir'): path = os.path.join(config.get('requests_dir'), 'index.html') if not os.path.exists(path): print("Requests directory not configured.") print("You can configure it using " "https://github.com/spesmilo/electrum-merchant") sys.exit(1) d.join() sys.exit(0) else: result = server.daemon(config_options) else: server = daemon.get_server(config) if server is not None: result = server.daemon(config_options) else: print("Daemon not running") sys.exit(1) else: # command line server = daemon.get_server(config) init_cmdline(config_options, server) if server is not None: result = server.run_cmdline(config_options) else: cmd = known_commands[cmdname] if cmd.requires_network: print("Daemon not running; try 'electrum-sv daemon start'") sys.exit(1) else: result = run_offline_command(config, config_options) # print result if isinstance(result, str): print(result) elif type(result) is dict and result.get('error'): print(result.get('error'), file=sys.stderr) elif result is not None: print(json_encode(result)) sys.exit(0)
def test_legacy_wallet_loading(storage_info: WalletStorageInfo) -> None: # When a wallet is composed of multiple files, we need to know which to load. wallet_filenames = [] if storage_info.kind != StorageKind.DATABASE: wallet_filenames.append(storage_info.filename) if storage_info.kind in (StorageKind.DATABASE, StorageKind.HYBRID): wallet_filenames.append(storage_info.filename + DATABASE_EXT) temp_dir = tempfile.mkdtemp() for _wallet_filename in wallet_filenames: source_wallet_path = os.path.join(TEST_WALLET_PATH, _wallet_filename) wallet_path = os.path.join(temp_dir, _wallet_filename) shutil.copyfile(source_wallet_path, wallet_path) wallet_filename = storage_info.filename wallet_path = os.path.join(temp_dir, wallet_filename) if "testnet" in wallet_filename: Net.set_to(SVTestnet) if storage_info.kind == StorageKind.HYBRID: pytest.xfail("old development database wallets not supported yet") password = None storage = WalletStorage(wallet_path) if "passworded" in wallet_filename: password = "******" text_store = storage.get_text_store() text_store.load_data(text_store.decrypt(password)) if "encrypted" in wallet_filename: password = "******" check_no_password = False storage.upgrade(password is not None, password) try: wallet = Wallet(storage) except FileNotFoundError as e: if sys.version_info[:3] >= (3, 8, 0): msg = "Could not find module 'libusb-1.0.dll' (or one of its dependencies)." if msg in e.args[0]: pytest.xfail("libusb DLL could not be found") return raise e except OSError as e: if sys.version_info[:3] < (3, 8, 0): if "The specified module could not be found" in e.args[1]: pytest.xfail("libusb DLL could not be found") return raise e old_password = password password = "******" wallet.update_password(password, old_password) if "standard" in wallet_filename: is_bip39 = "bip39" in wallet_filename check_legacy_parent_of_standard_wallet(wallet, is_bip39=is_bip39, password=password) elif "imported_privkey" in wallet_filename: check_legacy_parent_of_imported_privkey_wallet(wallet) elif "imported_address" in wallet_filename: check_legacy_parent_of_imported_address_wallet(wallet) elif "multisig" in wallet_filename: check_legacy_parent_of_multisig_wallet(wallet) elif "hardware" in wallet_filename: check_legacy_parent_of_hardware_wallet(wallet) else: raise Exception(f"unrecognised wallet file {wallet_filename}") if "testnet" in wallet_filename: Net.set_to(SVMainnet)
def main(): enforce_requirements() setup_windows_console() # The hook will only be used in the Qt GUI right now setup_thread_excepthook() # on osx, delete Process Serial Number arg generated for apps launched in Finder sys.argv = [x for x in sys.argv if not x.startswith('-psn')] # old 'help' syntax if len(sys.argv) > 1 and sys.argv[1] == 'help': sys.argv.remove('help') sys.argv.append('-h') # read arguments from stdin pipe and prompt for i, arg in enumerate(sys.argv): if arg == '-': if not sys.stdin.isatty(): sys.argv[i] = sys.stdin.read() break else: raise Exception('Cannot get argument from stdin') elif arg == '?': sys.argv[i] = input("Enter argument:") elif arg == ':': sys.argv[i] = prompt_password('Enter argument (will not echo):', False) # parse command line parser = get_parser() args = parser.parse_args() # config is an object passed to various constructors config_options = args.__dict__ config_options = { key: value for key, value in config_options.items() if value is not None and key not in config_variables.get(args.cmd, {}) } logs.set_level(config_options['verbose']) if config_options.get('server'): config_options['auto_connect'] = False config_options['cwd'] = os.getcwd() # fixme: this can probably be achieved with a runtime hook (pyinstaller) portable_base_path = None try: if startup.is_bundle and os.path.exists( os.path.join(sys._MEIPASS, 'is_portable')): config_options['portable'] = True # Ensure the wallet data is stored in the same directory as the executable. portable_base_path = os.path.dirname(sys.executable) except AttributeError: config_options['portable'] = False if config_options.get('portable'): if portable_base_path is None: # Default to the same directory the 'electrum-sv' script is in. portable_base_path = os.path.dirname(os.path.realpath(sys.argv[0])) config_options['electrum_sv_path'] = os.path.join( portable_base_path, 'electrum_sv_data') if config_options.get('file_logging'): if config_options.get('portable'): log_path = os.path.join(config_options['electrum_sv_path'], "logs") else: log_path = os.path.join(platform.user_dir(prefer_local=True), "logs") os.makedirs(log_path, exist_ok=True) log_path = os.path.join(log_path, time.strftime("%Y%m%d-%H%M%S") + ".log") logs.add_file_output(log_path) if config_options.get('testnet'): Net.set_to(SVTestnet) elif config_options.get('scalingtestnet'): Net.set_to(SVScalingTestnet) # check uri uri = config_options.get('url') if uri: if not web.is_URI(uri): print('unknown command:', uri, file=sys.stderr) sys.exit(1) config_options['url'] = uri # todo: defer this to gui config = SimpleConfig(config_options) cmdname = config.get('cmd') # Set the app state proxy if cmdname == 'gui': try: from electrumsv.gui.qt.app_state import QtAppStateProxy except ImportError as e: platform.missing_import(e) QtAppStateProxy(config, 'qt') elif cmdname == 'daemon' and 'daemon_app_module' in config_options: load_app_module(config_options['daemon_app_module'], config) else: AppStateProxy(config, 'cmdline') app_state.set_app(DefaultApp()) # run non-RPC commands separately if cmdname in ['create', 'restore']: run_non_RPC(config) sys.exit(0) if cmdname == 'gui': fd, server = daemon.get_fd_or_server(config) if fd is not None: run_app_with_daemon(fd, True, config_options) else: result = server.gui(config_options) elif cmdname == 'daemon': subcommand = config.get('subcommand') if subcommand in ['load_wallet']: init_daemon(config_options) if subcommand in [None, 'start']: fd, server = daemon.get_fd_or_server(config) if fd is not None: if not app_state.has_app(): print("No application present to run.") sys.exit(0) if subcommand == 'start': if not hasattr(os, "fork"): print( f"Starting the daemon is not supported on {sys.platform}." ) sys.exit(0) pid = os.fork() if pid: print("Starting daemon (PID %d)" % pid, file=sys.stderr) sys.exit(0) run_app_with_daemon(fd, False, config_options) else: result = server.daemon(config_options) else: server = daemon.get_server(config) if server is not None: result = server.daemon(config_options) else: print("Daemon not running") sys.exit(1) else: # command line server = daemon.get_server(config) init_cmdline(config_options, server) if server is not None: result = server.run_cmdline(config_options) else: cmd = known_commands[cmdname] if cmd.requires_network: print("Daemon not running; try 'electrum-sv daemon start'") sys.exit(1) else: result = run_offline_command(config, config_options) # print result if isinstance(result, str): print(result) elif type(result) is dict and result.get('error'): print(result.get('error'), file=sys.stderr) elif result is not None: print(json_encode(result)) sys.exit(0)
def tearDownClass(cls): super().tearDownClass() Net.set_to(SVMainnet)
def setUpClass(cls): super().setUpClass() Net.set_to(SVTestnet)
def setUp(self) -> None: Net.set_to(SVMainnet) self.storage = MockStorage() self.wallet = _Wallet(self.storage)
def test_create_and_broadcast_exception_handling(self, event_loop): Net.set_to(SVRegTestnet) p2pkh_object = SVRegTestnet.REGTEST_FUNDS_PUBLIC_KEY.to_address() async def main(): async with aiohttp.ClientSession() as session: # Todo - use websocket instead of sleeps time.sleep(6) # get tx history before tests to compare later result1 = await self.get_tx_history(session) error_code = result1.get('code') if error_code: assert False, result1 len_tx_hist_before = len(result1['history']) # get utxos result2 = await self.get_utxos(session) error_code = result2.get('code') if error_code: assert False, result2 utxos = result2['utxos'] # Prepare for two txs that use the same utxo P2PKH_OUTPUT = { "value": 100, "script_pubkey": p2pkh_object.to_script().to_hex() } # base tx payload1 = { "outputs": [P2PKH_OUTPUT], "password": "******", "utxos": [utxos[0]] } # trigger mempool conflict payload2 = { "outputs": [P2PKH_OUTPUT, P2PKH_OUTPUT], "password": "******", "utxos": [utxos[0]] } # trigger 'duplicate set' internal server error (same exact txid in tx cache) payload3 = { "outputs": [P2PKH_OUTPUT], "password": "******", "utxos": [utxos[0]] } # First tx result3 = await self.create_and_send(session, payload1) error_code = result3.get('code') if error_code: assert False, result3 # Trigger "mempool conflict" result4 = await self.create_and_send(session, payload2) error_code = result4.get('code') if not error_code: assert False, result4 assert result4['code'] == 40011 # Trigger 'duplicate set' internal server error (same exact txid in tx cache) result4 = await self.create_and_send(session, payload3) error_code = result4.get('code') if not error_code: assert False, result4 assert result4['code'] == 50000 # trigger insufficient coins P2PKH_OUTPUT = { "value": 1_000 * 100_000_000, "script_pubkey": p2pkh_object.to_script().to_hex() } payload2 = {"outputs": [P2PKH_OUTPUT], "password": "******"} result5 = await self.create_and_send(session, payload2) error_code = result5.get('code') if not error_code: assert False, result5 assert result5 == { 'code': 40006, 'message': 'You have insufficient coins for this transaction' } # Todo - use websocket instead of sleeps time.sleep(6) # check that only 1 new txs was created result6 = await self.get_tx_history(session) error_code = result6.get('code') if error_code: assert False, result6 len_tx_hist_after = len(result6['history']) # only one extra tx should exist (in the other cases, no tx should exist) assert len_tx_hist_before == (len_tx_hist_after - 1) event_loop.run_until_complete(main())