Exemple #1
0
def run_non_RPC(config):
    """Most commands should go through the daemon or RPC, especially commands that operate on
    wallets."""
    cmdname = config.get('cmd')

    def get_wallet_path() -> str:
        wallet_path = config.get_cmdline_wallet_filepath()
        if wallet_path is None:
            sys.exit("error: no wallet path provided")

        final_path = WalletStorage.canonical_path(wallet_path)
        if WalletStorage.files_are_matched_by_path(wallet_path):
            sys.exit(f"error: wallet already exists: {final_path}")

        return final_path

    if cmdname in {'create_wallet', 'create_account'}:
        if not config.cmdline_options.get('nopasswordcheck'):
            password = prompt_password("Password:"******"error: wallet creation requires a password")

        if cmdname == 'create_wallet':
            wallet_path = get_wallet_path()
            storage = WalletStorage.create(wallet_path, password)
            storage.close()
            print(f"Wallet saved in '{wallet_path}'")
            sys.exit(0)

        elif cmdname == 'create_account':
            wallet_path = config.get_cmdline_wallet_filepath()
            storage = WalletStorage.create(wallet_path, password)
            parent_wallet = Wallet(storage)

            # create an account for the Wallet (only random new seeds supported - no importing)
            text_type = KeystoreTextType.EXTENDED_PRIVATE_KEY

            text_match = os.getenv("ELECTRUMSV_ACCOUNT_XPRV")
            if not text_match:  # generate a random account seed
                data = urandom(64)
                coin = bitcoinx.BitcoinRegtest
                xprv = bitcoinx.BIP32PrivateKey._from_parts(
                    data[:32], data[32:], coin)
                text_match = xprv.to_extended_key_string()

            keystore = instantiate_keystore_from_text(text_type,
                                                      text_match,
                                                      password,
                                                      derivation_text=None,
                                                      passphrase=None,
                                                      watch_only=False)
            parent_wallet.create_account_from_keystore(keystore)
            print(f"New standard (bip32) account created for: '{wallet_path}'")
            sys.exit(0)

    else:
        sys.exit("error: unrecognised command")
Exemple #2
0
def check_legacy_parent_of_multisig_wallet(wallet: Wallet) -> None:
    assert len(wallet.get_accounts()) == 1
    account: MultisigAccount = wallet.get_accounts()[0]

    m = account.m
    n = account.n

    parent_keystores = wallet.get_keystores()
    assert len(parent_keystores) == 1
    keystore = parent_keystores[0]
    child_keystores = keystore.get_cosigner_keystores()
    assert len(child_keystores) == n
    parent_data = keystore.to_derivation_data()

    for i in range(n):
        masterkey_row = child_keystores[i].to_masterkey_row()
        assert masterkey_row.derivation_type == DerivationType.BIP32
        keystore_data = parent_data["cosigner-keys"][i][1]
        if len(keystore_data) == 3:
            assert keystore_data['seed'] is not None # == seed_words
            assert keystore_data['xpub'] is not None
            assert keystore_data['xprv'] is not None
        else:
            assert len(keystore_data) == 2
            assert keystore_data['xpub'] is not None
            assert keystore_data['xprv'] is None
Exemple #3
0
    def test_multisig(self, tmp_storage) -> None:
        wallet = Wallet(tmp_storage)

        seed_words = (
            'blast uniform dragon fiscal ensure vast young utility dinosaur abandon '
            + 'rookie sure')
        ks1 = from_seed(seed_words, '')
        ks2 = from_xpub(
            'xpub661MyMwAqRbcGfCPEkkyo5WmcrhTq8mi3xuBS7VEZ3LYvsgY1cCFDben' +
            'T33bdD12axvrmXhuX3xkAbKci3yZY9ZEk8vhLic7KNhLjqdh5ec')

        keystore = Multisig_KeyStore({'m': 2, 'n': 2, "cosigner-keys": []})
        keystore.add_cosigner_keystore(ks1)
        keystore.add_cosigner_keystore(ks2)

        assert not keystore.is_watching_only()
        assert 2 == len(keystore.get_cosigner_keystores())

        masterkey_row = wallet.create_masterkey_from_keystore(keystore)

        account_row = AccountRow(1, masterkey_row.masterkey_id,
                                 ScriptType.MULTISIG_BARE, 'text')
        account = MultisigAccount(wallet, account_row, [], [])
        wallet.register_account(account.get_id(), account)

        check_legacy_parent_of_multisig_wallet(wallet)
        check_create_keys(wallet, account_row.default_script_type)
Exemple #4
0
    def __init__(self, config, daemon, plugins):

        self.config = config
        self.network = daemon.network
        storage = WalletStorage(config.get_wallet_path())
        if not storage.file_exists():
            print("Wallet not found. try 'electrum-sv create'")
            exit()
        if storage.is_encrypted():
            password = getpass.getpass('Password:'******'')
        self.encoding = locale.getpreferredencoding()

        self.stdscr = curses.initscr()
        curses.noecho()
        curses.cbreak()
        curses.start_color()
        curses.use_default_colors()
        curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
        curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_CYAN)
        curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE)
        self.stdscr.keypad(1)
        self.stdscr.border(0)
        self.maxy, self.maxx = self.stdscr.getmaxyx()
        self.set_cursor(0)
        self.w = curses.newwin(10, 50, 5, 5)

        disable_verbose_logging()
        self.tab = 0
        self.pos = 0
        self.popup_pos = 0

        self.str_recipient = ""
        self.str_description = ""
        self.str_amount = ""
        self.str_fee = ""
        self.history = None

        if self.network:
            self.network.register_callback(self.update, ['updated'])

        self.tab_names = [
            _("History"),
            _("Send"),
            _("Receive"),
            _("Addresses"),
            _("Contacts"),
            _("Banner")
        ]
        self.num_tabs = len(self.tab_names)
Exemple #5
0
    def test_imported_privkey(self, tmp_storage) -> None:
        wallet = Wallet(tmp_storage)
        account = wallet.create_account_from_text_entries(KeystoreTextType.PRIVATE_KEYS,
            ScriptType.P2PKH,
            [ "KzMFjMC2MPadjvX5Cd7b8AKKjjpBSoRKUTpoAtN6B3J9ezWYyXS6" ],
            "password")

        keypairs = {'02c6467b7e621144105ed3e4835b0b4ab7e35266a2ae1c4f8baa19e9ca93452997':
            'KzMFjMC2MPadjvX5Cd7b8AKKjjpBSoRKUTpoAtN6B3J9ezWYyXS6'}
        check_legacy_parent_of_imported_privkey_wallet(wallet, keypairs=keypairs,
            password='******')
Exemple #6
0
 def test_imported_pubkey(self, tmp_storage) -> None:
     text = """
     15hETetDmcXm1mM4sEf7U2KXC9hDHFMSzz
     1GPHVTY8UD9my6jyP4tb2TYJwUbDetyNC6
     """
     wallet = Wallet(tmp_storage)
     account = wallet.create_account_from_text_entries(KeystoreTextType.ADDRESSES,
         ScriptType.NONE,
         [ "15hETetDmcXm1mM4sEf7U2KXC9hDHFMSzz", "1GPHVTY8UD9my6jyP4tb2TYJwUbDetyNC6" ],
         "password")
     check_legacy_parent_of_imported_address_wallet(wallet)
Exemple #7
0
    def run_and_get_wallet(self):
        path = self.storage.path
        if self.storage.requires_split():
            self.hide()
            msg = _(
                "The wallet '{}' contains multiple accounts, which are not supported.\n\n"
                "Do you want to split your wallet into multiple files?"
            ).format(path)
            if not self.question(msg):
                return
            file_list = '\n'.join(self.storage.split_accounts())
            msg = (_('Your accounts have been moved to') + ':\n' + file_list +
                   '\n\n' + _('Do you want to delete the old file') + ':\n' +
                   path)
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            return

        if self.storage.requires_upgrade():
            self.hide()
            msg = _(
                "The format of your wallet '%s' must be upgraded for ElectrumSV. "
                "This change will not be backward compatible" % path)
            if not self.question(msg):
                return
            self.storage.upgrade()
            self.wallet = Wallet(self.storage)
            return self.wallet

        action = self.storage.get_action()
        if action and action != 'new':
            self.hide()
            msg = _("The file '{}' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?").format(path)
            if not self.question(msg):
                if self.question(
                        _("Do you want to delete '{}'?").format(path)):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
        if action:
            # self.wallet is set in run
            self.run(action)
            return self.wallet

        self.wallet = Wallet(self.storage)
        return self.wallet
Exemple #8
0
    def run_and_get_wallet(self):
        path = self.storage.path
        if self.storage.requires_split():
            msg = _("The wallet '{}' contains multiple accounts, which are not supported.\n\n"
                    "Do you want to split your wallet into multiple files?").format(path)
            if not MessageBox.question(msg):
                return
            file_list = '\n'.join(self.storage.split_accounts())
            msg = (_('Your accounts have been moved to') + ':\n' + file_list + '\n\n' +
                   _('Do you want to delete the old file') + ':\n' + path)
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            return

        if self.storage.requires_upgrade():
            msg = _("The format of your wallet '%s' must be upgraded for ElectrumSV. "
                    "This change will not be backward compatible, "+
                    "and your existing wallet will be backed up. Proceed?") % path
            if not MessageBox.question(msg):
                return
            self.storage.upgrade()
            self.wallet = Wallet(self.storage)
            return self.wallet

        action = self.storage.get_action()
        if action and action != 'new':
            msg = _("The file '{}' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?").format(path)
            if not MessageBox.question(msg):
                if MessageBox.question(_("Do you want to delete '{}'?").format(path)):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
        if action:
            # self.wallet is set in run, unless they go back.
            self.run(action)
            if action == "new" and self.wallet:
               # We forceably save new wallets in order to get the initial state synced on disk
                # that the user can both find it if ESV crashes, and that the externally referenced
                # and encrypted data has synchronised persisted keys
                self.wallet.save_storage()
            return self.wallet

        self.wallet = Wallet(self.storage)
        return self.wallet
Exemple #9
0
    def test_imported_pubkey(self, tmp_storage) -> None:
        text = """
        15hETetDmcXm1mM4sEf7U2KXC9hDHFMSzz
        1GPHVTY8UD9my6jyP4tb2TYJwUbDetyNC6
        """
        wallet = Wallet(tmp_storage)
        account = ImportedAddressAccount.from_text(wallet, None, text)

        check_legacy_parent_of_imported_address_wallet(wallet)
Exemple #10
0
def check_legacy_parent_of_imported_privkey_wallet(wallet: Wallet,
        keypairs: Optional[Dict[str, str]]=None, password: Optional[str]=None) -> None:
    assert len(wallet.get_accounts()) == 1
    account: ImportedPrivkeyAccount = wallet.get_accounts()[0]

    parent_keystores = wallet.get_keystores()
    assert len(parent_keystores) == 0
    child_keystores = account.get_keystores()
    assert len(child_keystores) == 1
    assert child_keystores[0] is not None

    assert not child_keystores[0].has_masterkey()
    with pytest.raises(IncompatibleWalletError):
        child_keystores[0].to_masterkey_row()
    with pytest.raises(IncompatibleWalletError):
        child_keystores[0].to_derivation_data()
    keyinstance_datas = child_keystores[0].get_keyinstance_derivation_data()
    assert len(keyinstance_datas) == 1
    if keypairs is not None:
        for key_id, data in keyinstance_datas:
            assert pw_decode(data['prv'], password) == keypairs[data['pub']]
Exemple #11
0
def check_legacy_parent_of_standard_wallet(
        wallet: Wallet,
        seed_words: Optional[str] = None,
        is_bip39: bool = False,
        password: Optional[str] = None) -> None:
    assert len(wallet.get_accounts()) == 1
    account: StandardAccount = wallet.get_accounts()[0]

    parent_keystores = wallet.get_keystores()
    assert len(parent_keystores) == 1
    child_keystores = account.get_keystores()
    assert len(child_keystores) == 1
    assert parent_keystores[0] is child_keystores[0]

    assert password is not None
    assert not child_keystores[0].has_seed() or child_keystores[0].get_seed(
        password)
    assert type(child_keystores[0].get_passphrase(password)) is str
    assert child_keystores[0].get_master_private_key(password)

    keystore_data = parent_keystores[0].to_derivation_data()
    entry_count = 4
    if is_bip39:
        entry_count = 3
    assert len(keystore_data) == entry_count, keystore_data
    assert 'xpub' in keystore_data
    assert 'xprv' in keystore_data
    keystore_encrypted = False
    try:
        parent_keystores[0].check_password(None)
    except InvalidPassword:
        keystore_encrypted = True
    assert "encrypted" not in wallet.name() or keystore_encrypted
    if is_bip39:
        assert "seed" not in keystore_data
    else:
        if seed_words is None:
            assert "seed" in keystore_data
        else:
            assert keystore_data['seed'] == seed_words
Exemple #12
0
    def test_old(self, tmp_storage) -> None:
        seed_words = ('powerful random nobody notice nothing important '+
            'anyway look away hidden message over')
        child_keystore = from_seed(seed_words, '')
        assert isinstance(child_keystore, Old_KeyStore)

        wallet = Wallet(tmp_storage)
        masterkey_row = wallet.create_masterkey_from_keystore(child_keystore)
        account_row = AccountRow(1, masterkey_row.masterkey_id, ScriptType.P2PKH, '...')
        account = StandardAccount(wallet, account_row, [], [])
        wallet.register_account(account.get_id(), account)

        parent_keystores = wallet.get_keystores()
        assert len(parent_keystores) == 1
        child_keystores = account.get_keystores()
        assert len(child_keystores) == 1
        assert parent_keystores[0] is child_keystores[0]

        masterkey_row = parent_keystores[0].to_masterkey_row()
        assert masterkey_row.derivation_type == DerivationType.ELECTRUM_OLD
        keystore_data = parent_keystores[0].to_derivation_data()
        assert len(keystore_data) == 3
        assert 'mpk' in keystore_data
        assert 'seed' in keystore_data
        assert 'subpaths' in keystore_data

        check_create_keys(wallet, account_row.default_script_type)
Exemple #13
0
    def __init__(self, config, daemon, plugins):
        self.config = config
        self.network = daemon.network
        storage = WalletStorage(config.get_wallet_path())
        if not storage.file_exists:
            print("Wallet not found. try 'electrum-sv create'")
            exit()
        if storage.is_encrypted():
            password = getpass.getpass('Password:'******'updated', 'banner'])
        self.commands = [
            _("[h] - displays this help text"),
            _("[i] - display transaction history"),
            _("[o] - enter payment order"),
            _("[p] - print stored payment order"),
            _("[s] - send stored payment order"),
            _("[r] - show own receipt addresses"),
            _("[c] - display contacts"),
            _("[b] - print server banner"),
            _("[q] - quit"),
        ]
        self.num_commands = len(self.commands)
Exemple #14
0
def run_offline_command(config, config_options):
    cmdname = config.get('cmd')
    cmd = known_commands[cmdname]
    password = config_options.get('password')
    if cmd.requires_wallet:
        storage = WalletStorage(config.get_wallet_path())
        if storage.is_encrypted():
            storage.decrypt(password)
        wallet = Wallet(storage)
    else:
        wallet = None
    # check password
    if cmd.requires_password and storage.get('use_encryption'):
        try:
            seed = wallet.check_password(password)
        except InvalidPassword:
            print("Error: This password does not decode this wallet.")
            sys.exit(1)
    if cmd.requires_network:
        print("Warning: running command offline")
    # arguments passed to function
    args = [config.get(x) for x in cmd.params]
    # decode json arguments
    if cmdname not in ('setconfig', ):
        args = [json_decode(arg) for arg in args]
    # options
    kwargs = {}
    for x in cmd.options:
        kwargs[x] = (config_options.get(x)
                     if x in ['password', 'new_password'] else config.get(x))
    cmd_runner = Commands(config, wallet, None)
    func = getattr(cmd_runner, cmd.name)
    result = func(*args, **kwargs)
    # save wallet
    if wallet:
        wallet.storage.write()
    return result
Exemple #15
0
    def wallet_widgets(self, wallet: Wallet):
        usechange_cb = QCheckBox(_('Use change addresses'))
        usechange_cb.setChecked(wallet.get_use_change())
        usechange_cb.setEnabled(app_state.config.is_modifiable('use_change'))
        usechange_cb.setToolTip(
            _('Using a different change key each time improves your privacy by '
              'making it more difficult for others to analyze your transactions.'
              ))

        def on_usechange(state):
            usechange_result = state == Qt.Checked
            if wallet.get_use_change() != usechange_result:
                wallet.set_use_change(usechange_result)
                multiple_cb.setEnabled(wallet.get_use_change())

        usechange_cb.stateChanged.connect(on_usechange)

        multiple_cb = QCheckBox(_('Use multiple change addresses'))
        multiple_cb.setChecked(wallet.get_multiple_change())
        multiple_cb.setEnabled(wallet.get_use_change())
        multiple_cb.setToolTip('\n'.join([
            _('In some cases, use up to 3 change keys in order to break '
              'up large coin amounts and obfuscate the recipient key.'),
            _('This may result in higher transactions fees.')
        ]))

        def on_multiple(state):
            multiple = state == Qt.Checked
            if wallet.get_multiple_change() != multiple:
                wallet.set_multiple_change(multiple)

        multiple_cb.stateChanged.connect(on_multiple)

        return [
            (usechange_cb, ),
            (multiple_cb, ),
        ]
Exemple #16
0
def check_legacy_parent_of_hardware_wallet(wallet: Wallet) -> None:
    assert len(wallet.get_accounts()) == 1
    child_account = wallet.get_accounts()[0]

    parent_keystores = wallet.get_keystores()
    assert len(parent_keystores) == 1
    child_keystores = child_account.get_keystores()
    assert len(child_keystores) == 1
    assert parent_keystores[0] is child_keystores[0]

    masterkey_row = parent_keystores[0].to_masterkey_row()
    assert masterkey_row.derivation_type == DerivationType.HARDWARE
    keystore_data = parent_keystores[0].to_derivation_data()
    # General hardware wallet.
    entry_count = 5
    if keystore_data['hw_type'] == "ledger":
        # Ledger wallets extend the keystore.
        assert "cfg" in keystore_data
        entry_count = 6
    assert len(keystore_data) == entry_count
    assert 'hw_type' in keystore_data
    assert 'label' in keystore_data
    assert "derivation" in keystore_data
    assert "subpaths" in keystore_data
Exemple #17
0
    def test_imported_privkey(self, tmp_storage) -> None:
        text = """
        KzMFjMC2MPadjvX5Cd7b8AKKjjpBSoRKUTpoAtN6B3J9ezWYyXS6
        """
        wallet = Wallet(tmp_storage)
        script_type = ScriptType.P2PKH
        account = ImportedPrivkeyAccount.from_text(wallet, None, script_type,
                                                   'password', text)

        keypairs = {
            '02c6467b7e621144105ed3e4835b0b4ab7e35266a2ae1c4f8baa19e9ca93452997':
            'KzMFjMC2MPadjvX5Cd7b8AKKjjpBSoRKUTpoAtN6B3J9ezWYyXS6'
        }
        check_legacy_parent_of_imported_privkey_wallet(wallet,
                                                       keypairs=keypairs,
                                                       password='******')
Exemple #18
0
    def test_standard_electrum(self, tmp_storage) -> None:
        password = '******'
        seed_words = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song'
        child_keystore = from_seed(seed_words, '')

        wallet = Wallet(tmp_storage)
        masterkey_row = wallet.create_masterkey_from_keystore(child_keystore)
        wallet.update_password(password)

        account_row = AccountRow(1, masterkey_row.masterkey_id, ScriptType.P2PKH, '...')
        account = StandardAccount(wallet, account_row, [], [])
        wallet.register_account(account.get_id(), account)

        check_legacy_parent_of_standard_wallet(wallet, password=password)
        check_create_keys(wallet, account_row.default_script_type)
Exemple #19
0
def run_offline_command(config, config_options):
    cmdname = config.get('cmd')
    cmd = known_commands[cmdname]
    password = config_options.get('password')
    if cmd.requires_wallet:
        wallet_path = config.get_wallet_path()
        if not WalletStorage.files_are_matched_by_path(wallet_path):
            print("Error: wallet does not exist at given path")
            sys.exit(1)
        storage = WalletStorage(wallet_path)
        wallet = Wallet(storage)
    else:
        wallet = None
    if cmd.requires_password:
        try:
            wallet.check_password(password)
        except InvalidPassword:
            print(
                "Error: This password cannot access the wallet's private data."
            )
            sys.exit(1)
    if cmd.requires_network:
        print("Warning: running command offline")
    # arguments passed to function
    args = [config.get(x) for x in cmd.params]
    # decode json arguments
    if cmdname not in ('setconfig', ):
        args = [json_decode(arg) for arg in args]
    # options
    kwargs = {}
    for x in cmd.options:
        kwargs[x] = (config_options.get(x)
                     if x in ['password', 'new_password'] else config.get(x))
    cmd_runner = Commands(config, wallet, None)
    func = getattr(cmd_runner, cmd.name)
    result = func(*args, **kwargs)
    # save wallet
    if wallet:
        wallet.save_storage()
    return result
Exemple #20
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)
Exemple #21
0
def check_legacy_parent_of_imported_address_wallet(wallet: Wallet) -> None:
    assert len(wallet.get_accounts()) == 1
    account: ImportedAddressAccount = wallet.get_accounts()[0]

    assert len(wallet.get_keystores()) == 0
    assert len(account.get_keystores()) == 0
Exemple #22
0
    def run_and_get_wallet(self):
        vbox = QVBoxLayout()
        hbox = QHBoxLayout()
        hbox.addWidget(QLabel(_('Wallet') + ':'))
        self.name_e = QLineEdit()
        hbox.addWidget(self.name_e)
        button = QPushButton(_('Choose...'))
        hbox.addWidget(button)
        vbox.addLayout(hbox)

        self.msg_label = QLabel('')
        vbox.addWidget(self.msg_label)

        hbox2 = QHBoxLayout()
        self.pw_e = PasswordLineEdit()
        self.pw_e.setMinimumWidth(200)
        self.pw_label = QLabel(_('Password') + ':')
        self.pw_label.setAlignment(Qt.AlignTop)
        hbox2.addWidget(self.pw_label)
        hbox2.addWidget(self.pw_e)
        hbox2.addStretch()
        vbox.addLayout(hbox2)
        self._set_standard_layout(vbox,
                                  title=_('ElectrumSV wallet'),
                                  back_text=_(MSG_BUTTON_CANCEL))

        wallet_folder = os.path.dirname(self.storage.path)

        def on_choose():
            path, __ = QFileDialog.getOpenFileName(self,
                                                   "Select your wallet file",
                                                   wallet_folder)
            if path:
                self.name_e.setText(path)

        def on_filename(filename):
            path = os.path.join(wallet_folder, filename)
            try:
                self.storage = WalletStorage(path, manual_upgrades=True)
                self.next_button.setEnabled(True)
            except IOError:
                self.storage = None
                self.next_button.setEnabled(False)
            if self.storage:
                if not self.storage.file_exists():
                    msg =_("This file does not exist.") + '\n' \
                          + _("Press 'Next' to create this wallet, or choose another file.")
                    pw = False
                elif self.storage.file_exists() and self.storage.is_encrypted(
                ):
                    msg = '\n'.join([
                        _("This file is encrypted."),
                        _('Enter your password or choose another file.'),
                    ])
                    pw = True
                else:
                    msg = _("Press 'Next' to open this wallet.")
                    pw = False
            else:
                msg = _('Cannot read file')
                pw = False
            self.msg_label.setText(msg)
            if pw:
                self.pw_label.show()
                self.pw_e.show()
                self.pw_e.setFocus()
            else:
                self.pw_label.hide()
                self.pw_e.hide()

        button.clicked.connect(on_choose)
        self.name_e.textChanged.connect(on_filename)
        n = os.path.basename(self.storage.path)
        self.name_e.setText(n)

        while True:
            if self.storage.file_exists() and not self.storage.is_encrypted():
                break
            if self.loop.exec_() != 2:  # 2 = next
                return
            if not self.storage.file_exists():
                break
            if self.storage.file_exists() and self.storage.is_encrypted():
                password = self.pw_e.text()
                try:
                    self.storage.decrypt(password)
                    self.pw_e.setText('')
                    break
                except InvalidPassword as e:
                    QMessageBox.information(None, _('Error'), str(e))
                    continue
                except Exception as e:
                    logger.exception("decrypting storage")
                    QMessageBox.information(None, _('Error'), str(e))
                    return

        path = self.storage.path
        if self.storage.requires_split():
            self.hide()
            msg = _(
                "The wallet '{}' contains multiple accounts, which are not supported.\n\n"
                "Do you want to split your wallet into multiple files?"
            ).format(path)
            if not self.question(msg):
                return
            file_list = '\n'.join(self.storage.split_accounts())
            msg = (_('Your accounts have been moved to') + ':\n' + file_list +
                   '\n\n' + _('Do you want to delete the old file') + ':\n' +
                   path)
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            return

        if self.storage.requires_upgrade():
            self.hide()
            msg = _(
                "The format of your wallet '%s' must be upgraded for ElectrumSV. "
                "This change will not be backward compatible" % path)
            if not self.question(msg):
                return
            self.storage.upgrade()
            self.wallet = Wallet(self.storage)
            return self.wallet

        action = self.storage.get_action()
        if action and action != 'new':
            self.hide()
            msg = _("The file '{}' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?").format(path)
            if not self.question(msg):
                if self.question(
                        _("Do you want to delete '{}'?").format(path)):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
        if action:
            # self.wallet is set in run
            self.run(action)
            return self.wallet

        self.wallet = Wallet(self.storage)
        return self.wallet
Exemple #23
0
    def wallet_widgets(self, wallet: Wallet, tab: QWidget) -> None:
        usechange_cb = QCheckBox(_('Use change addresses'))
        usechange_cb.setChecked(wallet.get_use_change())
        usechange_cb.setEnabled(app_state.config.is_modifiable('use_change'))
        usechange_cb.setToolTip(
            _('Using a different change key each time improves your privacy by '
              'making it more difficult for others to analyze your transactions.'
              ))

        def on_usechange(state):
            usechange_result = state == Qt.Checked
            if wallet.get_use_change() != usechange_result:
                wallet.set_use_change(usechange_result)
                multiple_cb.setEnabled(wallet.get_use_change())

        usechange_cb.stateChanged.connect(on_usechange)

        multiple_cb = QCheckBox(_('Use multiple change addresses'))
        multiple_cb.setChecked(wallet.get_multiple_change())
        multiple_cb.setEnabled(wallet.get_use_change())
        multiple_cb.setToolTip('\n'.join([
            _('In some cases, use up to 3 change keys in order to break '
              'up large coin amounts and obfuscate the recipient key.'),
            _('This may result in higher transactions fees.')
        ]))

        def on_multiple(state):
            multiple = state == Qt.Checked
            if wallet.get_multiple_change() != multiple:
                wallet.set_multiple_change(multiple)

        multiple_cb.stateChanged.connect(on_multiple)

        options_box = QGroupBox()
        options_vbox = QVBoxLayout()
        options_box.setLayout(options_vbox)
        options_vbox.addWidget(usechange_cb)
        options_vbox.addWidget(multiple_cb)

        transaction_cache_size = wallet.get_cache_size_for_tx_bytedata()
        # nz_label = HelpLabel(_('Transaction Cache Size (MB)') + ':',
        #     _("This allows setting a per-wallet limit on the amount of transaction data cached "
        #     "in memory. A value of 0 will disable the cache, and setting low values can cause "
        #     "wallet slowness due to continual fetching of transaction data from the database."))
        nz_modifiable = app_state.config.is_modifiable(
            'tx_bytedata_cache_size')
        nz = QSpinBox()
        nz.setAlignment(Qt.AlignRight)
        nz.setMinimum(MINIMUM_TXDATA_CACHE_SIZE_MB)
        nz.setMaximum(MAXIMUM_TXDATA_CACHE_SIZE_MB)
        nz.setValue(transaction_cache_size)
        nz.setEnabled(nz_modifiable)

        def on_nz():
            value = nz.value()
            # This will not resize the cache, as we do not want to be doing it with every
            # change and some changes may be bad to actually put in place.
            wallet.set_cache_size_for_tx_bytedata(value)

        nz.valueChanged.connect(on_nz)

        tx_cache_layout = QHBoxLayout()
        tx_cache_layout.setSpacing(15)
        tx_cache_layout.addWidget(nz)
        tx_cache_layout.addWidget(QLabel(_("MiB")))

        form = FormSectionWidget(minimum_label_width=120)
        form.add_row(_('Options'), options_box, True)
        form.add_row(_('Transaction Cache Size'), tx_cache_layout)

        vbox = QVBoxLayout()
        vbox.addWidget(form)
        vbox.addStretch(1)
        tab.setLayout(vbox)
 def _load_wallet(self) -> None:
     wizard: WalletWizard = self.wizard()
     wallet_path = wizard.get_wallet_path()
     wizard.set_wallet(Wallet(WalletStorage(wallet_path)))
Exemple #25
0
def check_parent_of_blank_wallet(wallet: Wallet) -> None:
    assert len(wallet.get_accounts()) == 0
    parent_keystores = wallet.get_keystores()
    assert len(parent_keystores) == 0
Exemple #26
0
def check_create_keys(wallet: Wallet, account_script_type: ScriptType) -> None:
    def check_rows(rows: List[KeyInstanceRow], script_type: ScriptType) -> None:
        for row in rows:
            assert isinstance(row.keyinstance_id, int)
            assert account.get_id() == row.account_id
            assert 1 == row.masterkey_id
            assert script_type == row.script_type
            assert DerivationType.BIP32_SUBPATH == row.derivation_type
            assert None is row.description

    accounts = wallet.get_accounts()
    assert len(accounts) == 1
    account = accounts[0]
    assert [] == account.get_existing_fresh_keys(RECEIVING_SUBPATH)
    assert [] == account.get_existing_fresh_keys(CHANGE_SUBPATH)
    assert account_script_type == account.get_default_script_type()

    keyinstances: List[KeyInstanceRow] = []
    keyinstance_ids: Set[int] = set()

    for count in (0, 1, 5):
        new_keyinstances = account.create_keys(count, RECEIVING_SUBPATH)
        assert count == len(new_keyinstances)
        check_rows(new_keyinstances, account_script_type)
        keyinstance_ids |= set(keyinstance.keyinstance_id for keyinstance in new_keyinstances)
        keyinstances.extend(new_keyinstances)
        assert len(keyinstance_ids) == len(keyinstances)
        assert [] == account.get_existing_fresh_keys(RECEIVING_SUBPATH)

    for count in (0, 1, 5):
        last_row = keyinstances[-1]
        last_index = account.get_derivation_path(last_row.keyinstance_id)[-1]
        next_index = account.get_next_derivation_index(RECEIVING_SUBPATH)
        assert next_index == last_index  + 1

        try:
            new_keyinstances = account.create_keys_until(
                RECEIVING_SUBPATH + (next_index + count - 1,))
        except AssertionError:
            assert 0 == count
            continue
        assert 0 != count
        assert count == len(new_keyinstances)
        check_rows(new_keyinstances, account_script_type)

        keyinstance_ids |= set(keyinstance.keyinstance_id for keyinstance in new_keyinstances)
        keyinstances.extend(new_keyinstances)
        assert len(keyinstance_ids) == len(keyinstances)
        assert [] == account.get_existing_fresh_keys(RECEIVING_SUBPATH)

    keyinstance_batches: List[List[KeyInstanceRow]] = []
    for count in (0, 1, 5):
        new_keyinstances = account.get_fresh_keys(RECEIVING_SUBPATH, count)
        assert count == len(new_keyinstances)
        assert new_keyinstances == account.get_existing_fresh_keys(RECEIVING_SUBPATH)
        check_rows(new_keyinstances, ScriptType.NONE)
        # Verify each batch includes the last batch and the extra created keys.
        if len(keyinstance_batches) > 0:
            last_keyinstances = keyinstance_batches[-1]
            assert last_keyinstances == new_keyinstances[:len(last_keyinstances)]
        keyinstance_batches.append(new_keyinstances)
Exemple #27
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 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

    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 _load_wallet(self) -> None:
     wizard: WalletWizard = self.wizard()
     if wizard.get_wallet() is not None:
         return None
     wallet_path = wizard.get_wallet_path()
     wizard.set_wallet(Wallet(WalletStorage(wallet_path)))
Exemple #29
0
def run_non_RPC(config):
    cmdname = config.get('cmd')

    storage = WalletStorage(config.get_wallet_path())
    if storage.file_exists():
        sys.exit("Error: Remove the existing wallet first!")

    def password_dialog():
        return prompt_password(
            "Password (hit return if you do not wish to encrypt your wallet):")

    if cmdname == 'restore':
        text = config.get('text').strip()
        passphrase = config.get('passphrase', '')
        password = password_dialog() if keystore.is_private(text) else None
        if keystore.is_address_list(text):
            wallet = ImportedAddressWallet.from_text(storage, text)
        elif keystore.is_private_key_list(text):
            wallet = ImportedPrivkeyWallet.from_text(storage, text, password)
        else:
            if keystore.is_seed(text):
                k = keystore.from_seed(text, passphrase, False)
            elif keystore.is_master_key(text):
                k = keystore.from_master_key(text)
            else:
                sys.exit("Error: Seed or key not recognized")
            if password:
                k.update_password(None, password)
            storage.put('keystore', k.dump())
            storage.put('wallet_type', 'standard')
            storage.put('use_encryption', bool(password))
            storage.write()
            wallet = Wallet(storage)
        if not config.get('offline'):
            network = Network(config)
            network.start()
            wallet.start_threads(network)
            print("Recovering wallet...")
            wallet.synchronize()
            wallet.wait_until_synchronized()
            msg = ("Recovery successful" if wallet.is_found() else
                   "Found no history for this wallet")
        else:
            msg = ("This wallet was restored offline. "
                   "It may contain more addresses than displayed.")
        print(msg)

    elif cmdname == 'create':
        password = password_dialog()
        passphrase = config.get('passphrase', '')
        seed_type = 'standard'
        seed = Mnemonic('en').make_seed(seed_type)
        k = keystore.from_seed(seed, passphrase, False)
        storage.put('keystore', k.dump())
        storage.put('wallet_type', 'standard')
        wallet = Wallet(storage)
        wallet.update_password(None, password, True)
        wallet.synchronize()
        print("Your wallet generation seed is:\n\"%s\"" % seed)
        print("Please keep it in a safe place; if you lose it, "
              "you will not be able to restore your wallet.")

    wallet.storage.write()
    print("Wallet saved in '%s'" % wallet.storage.path)
    sys.exit(0)
Exemple #30
0
class ElectrumGui:
    def __init__(self, config, daemon, plugins):
        self.config = config
        self.network = daemon.network
        storage = WalletStorage(config.get_wallet_path())
        if not storage.file_exists:
            print("Wallet not found. try 'electrum-sv create'")
            exit()
        if storage.is_encrypted():
            password = getpass.getpass('Password:'******'updated', 'banner'])
        self.commands = [
            _("[h] - displays this help text"),
            _("[i] - display transaction history"),
            _("[o] - enter payment order"),
            _("[p] - print stored payment order"),
            _("[s] - send stored payment order"),
            _("[r] - show own receipt addresses"),
            _("[c] - display contacts"),
            _("[b] - print server banner"),
            _("[q] - quit"),
        ]
        self.num_commands = len(self.commands)

    def on_network(self, event, *args):
        if event == 'updated':
            self.updated()
        elif event == 'banner':
            self.print_banner()

    def main_command(self):
        self.print_balance()
        c = input("enter command: ")
        if c == "h": self.print_commands()
        elif c == "i": self.print_history()
        elif c == "o": self.enter_order()
        elif c == "p": self.print_order()
        elif c == "s": self.send_order()
        elif c == "r": self.print_addresses()
        elif c == "c": self.print_contacts()
        elif c == "b": self.print_banner()
        elif c == "n": self.network_dialog()
        elif c == "e": self.settings_dialog()
        elif c == "q": self.done = 1
        else: self.print_commands()

    def updated(self):
        s = self.get_balance()
        if s != self.last_balance:
            print(s)
        self.last_balance = s
        return True

    def print_commands(self):
        self.print_list(self.commands, "Available commands")

    def print_history(self):
        width = [20, 40, 14, 14]
        delta = (80 - sum(width) - 4) / 3
        format_str = ("%" + "%d" % width[0] + "s" + "%" + "%d" %
                      (width[1] + delta) + "s" + "%" + "%d" %
                      (width[2] + delta) + "s" + "%" + "%d" %
                      (width[3] + delta) + "s")
        messages = []

        for item in self.wallet.get_history():
            tx_hash, height, conf, timestamp, delta, balance = item
            if conf:
                try:
                    time_str = datetime.datetime.fromtimestamp(
                        timestamp).isoformat(' ')[:-3]
                except Exception:
                    time_str = "unknown"
            else:
                time_str = 'unconfirmed'

            label = self.wallet.get_label(tx_hash)
            messages.append(
                format_str %
                (time_str, label, format_satoshis(delta, whitespaces=True),
                 format_satoshis(balance, whitespaces=True)))

        self.print_list(
            messages[::-1], format_str %
            (_("Date"), _("Description"), _("Amount"), _("Balance")))

    def print_balance(self):
        print(self.get_balance())

    def get_balance(self):
        if self.wallet.network.is_connected():
            if not self.wallet.up_to_date:
                msg = _("Synchronizing...")
            else:
                c, u, x = self.wallet.get_balance()
                msg = _("Balance") + ": %f  " % (Decimal(c) / COIN)
                if u:
                    msg += "  [%f unconfirmed]" % (Decimal(u) / COIN)
                if x:
                    msg += "  [%f unmatured]" % (Decimal(x) / COIN)
        else:
            msg = _("Not connected")

        return msg

    def print_contacts(self):
        messages = [
            "%20s   %45s " % (x[0], x[1][1]) for x in self.contacts.items()
        ]
        self.print_list(messages, "%19s  %25s " % ("Key", "Value"))

    def print_addresses(self):
        messages = [
            "%30s    %30s       " % (addr, self.wallet.labels.get(addr, ""))
            for addr in self.wallet.get_addresses()
        ]
        self.print_list(messages, "%19s  %25s " % ("Address", "Label"))

    def print_order(self):
        print("send order to " + self.str_recipient + ", amount: " +
              self.str_amount + "\nfee: " + self.str_fee + ", desc: " +
              self.str_description)

    def enter_order(self):
        self.str_recipient = input("Pay to: ")
        self.str_description = input("Description : ")
        self.str_amount = input("Amount: ")
        self.str_fee = input("Fee: ")

    def send_order(self):
        self.do_send()

    def print_banner(self):
        for i, x in enumerate(self.wallet.network.banner.split('\n')):
            print(x)

    def print_list(self, lst, firstline):
        lst = list(lst)
        self.maxpos = len(lst)
        if not self.maxpos: return
        print(firstline)
        for i in range(self.maxpos):
            msg = lst[i] if i < len(lst) else ""
            print(msg)

    def main(self):
        while self.done == 0:
            self.main_command()

    def do_send(self):
        if not Address.is_valid(self.str_recipient):
            print(_('Invalid Bitcoin address'))
            return
        try:
            amount = int(Decimal(self.str_amount) * COIN)
        except Exception:
            print(_('Invalid Amount'))
            return
        try:
            fee = int(Decimal(self.str_fee) * COIN)
        except Exception:
            print(_('Invalid Fee'))
            return

        if self.wallet.has_password():
            password = self.password_dialog()
            if not password:
                return
        else:
            password = None

        c = ""
        while c != "y":
            c = input("ok to send (y/n)?")
            if c == "n": return

        try:
            tx = self.wallet.mktx([(TYPE_ADDRESS, self.str_recipient, amount)],
                                  password, self.config, fee)
        except Exception as e:
            print(str(e))
            return

        if self.str_description:
            self.wallet.labels[tx.txid()] = self.str_description

        print(_("Please wait..."))
        status, msg = self.network.broadcast_transaction(tx)

        if status:
            print(_('Payment sent.'))
            #self.do_clear()
            #self.update_contacts_tab()
        else:
            print(_('Error'))

    def network_dialog(self):
        print(
            "use 'electrum-sv setconfig server/proxy' to change your network settings"
        )
        return True

    def settings_dialog(self):
        print("use 'electrum-sv setconfig' to change your settings")
        return True

    def password_dialog(self):
        return getpass.getpass()

#   XXX unused

    def run_receive_tab(self, c):
        #if c == 10:
        #    out = self.run_popup('Address', ["Edit label", "Freeze", "Prioritize"])
        return

    def run_contacts_tab(self, c):
        pass