Пример #1
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)
Пример #2
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)
Пример #3
0
 def _create_multisig_wallet(self, ks1, ks2):
     keystore = Multisig_KeyStore({ 'm': 2, 'n': 2, "cosigner-keys": [] })
     keystore.add_cosigner_keystore(ks1)
     keystore.add_cosigner_keystore(ks2)
     masterkey_row = self.wallet.create_masterkey_from_keystore(keystore)
     account_row = AccountRow(1, masterkey_row.masterkey_id, ScriptType.MULTISIG_P2SH, 'text')
     account = MultisigAccount(self.wallet, account_row, [], [])
     self.wallet.register_account(account.get_id(), account)
     account.synchronize()
     return account
Пример #4
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)
Пример #5
0
def test_remove_transaction(mocker) -> None:
    state = MockAppState()
    # Mocked out startup junk for AbstractAccount initialization.
    mocker.patch.object(state, "async_", return_value=NotImplemented)
    mocker.patch("electrumsv.wallet_database.tables.PaymentRequestTable.read").return_value = []

    account_row = AccountRow(ACCOUNT_ID, MASTERKEY_ID, ScriptType.P2PKH, "ACCOUNT 1")
    keyinstance_rows = [
        KeyInstanceRow(KEYINSTANCE_ID+1, ACCOUNT_ID, MASTERKEY_ID, DerivationType.BIP32,
            b'111', ScriptType.P2PKH, KeyInstanceFlag.IS_ACTIVE, None),
        KeyInstanceRow(KEYINSTANCE_ID+2, ACCOUNT_ID, MASTERKEY_ID, DerivationType.BIP32,
            b'111', ScriptType.P2PKH, KeyInstanceFlag.IS_ACTIVE, None),
        KeyInstanceRow(KEYINSTANCE_ID+3, ACCOUNT_ID, MASTERKEY_ID, DerivationType.BIP32,
            b'111', ScriptType.P2PKH, KeyInstanceFlag.IS_ACTIVE, None),
    ]
    transactionoutput_rows = [
        TransactionOutputRow(TX_HASH_1, 1, 100, KEYINSTANCE_ID+1, TransactionOutputFlag.IS_SPENT),
        TransactionOutputRow(TX_HASH_1, 2, 100, KEYINSTANCE_ID+2, TransactionOutputFlag.IS_SPENT),
        TransactionOutputRow(TX_HASH_2, 1, 200, KEYINSTANCE_ID+3, TransactionOutputFlag.NONE),
    ]

    wallet = MockWallet()
    account = CustomAccount(wallet, account_row, keyinstance_rows, transactionoutput_rows)

    def fake_get_transaction(tx_hash: bytes):
        tx = unittest.mock.Mock()
        tx.inputs = [ FakeTxin(TX_HASH_1, 1), FakeTxin(TX_HASH_1, 2) ]
        # We don't actually use what's in here, it's just used to mark the available outputs.
        tx.outputs = [ NotImplemented ]
        return tx
    wallet._transaction_cache.get_transaction = fake_get_transaction
    def fake_update_transactionoutput_flags(rows: List[Tuple[TransactionOutputFlag, bytes, int]]):
        assert 2 == len(rows)
        assert rows[0][0] == TransactionOutputFlag.NONE
        assert rows[0][1] == transactionoutput_rows[0].tx_hash
        assert rows[0][2] == transactionoutput_rows[0].tx_index
        assert rows[1][0] == TransactionOutputFlag.NONE
        assert rows[1][1] == transactionoutput_rows[1].tx_hash
        assert rows[1][2] == transactionoutput_rows[1].tx_index
    wallet.update_transactionoutput_flags = fake_update_transactionoutput_flags
    mocker.patch("electrumsv.wallet_database.tables.TransactionOutputTable.read").return_value = [
        transactionoutput_rows[0], transactionoutput_rows[1],
    ]
    account._remove_transaction(TX_HASH_2)
Пример #6
0
def test_table_keyinstances_crud(db_context: DatabaseContext) -> None:
    table = KeyInstanceTable(db_context)
    assert [] == table.read()

    table._get_current_timestamp = lambda: 10

    KEYINSTANCE_ID = 0
    ACCOUNT_ID = 10
    MASTERKEY_ID = 20
    DERIVATION_DATA1 = b'111'
    DERIVATION_DATA2 = b'222'
    SCRIPT_TYPE = 40

    line1 = KeyInstanceRow(KEYINSTANCE_ID + 1, ACCOUNT_ID + 1,
                           MASTERKEY_ID + 1, DerivationType.BIP32,
                           DERIVATION_DATA1, SCRIPT_TYPE + 1, True, None)
    line2 = KeyInstanceRow(KEYINSTANCE_ID + 2, ACCOUNT_ID + 1,
                           MASTERKEY_ID + 1, DerivationType.HARDWARE,
                           DERIVATION_DATA2, SCRIPT_TYPE + 2, True, None)

    # No effect: The masterkey foreign key constraint will fail as the masterkey does not exist.
    with pytest.raises(sqlite3.IntegrityError):
        with SynchronousWriter() as writer:
            table.create([line1], completion_callback=writer.get_callback())
            assert not writer.succeeded()

    # Satisfy the masterkey foreign key constraint by creating the masterkey.
    mktable = MasterKeyTable(db_context)
    with SynchronousWriter() as writer:
        mktable.create([MasterKeyRow(MASTERKEY_ID + 1, None, 2, b'111')],
                       completion_callback=writer.get_callback())
        assert writer.succeeded()

    # No effect: The account foreign key constraint will fail as the account does not exist.
    with pytest.raises(sqlite3.IntegrityError):
        with SynchronousWriter() as writer:
            table.create([line1], completion_callback=writer.get_callback())
            assert not writer.succeeded()

    # Satisfy the account foreign key constraint by creating the account.
    acctable = AccountTable(db_context)
    with SynchronousWriter() as writer:
        acctable.create([
            AccountRow(ACCOUNT_ID + 1, MASTERKEY_ID + 1, ScriptType.P2PKH,
                       'name')
        ],
                        completion_callback=writer.get_callback())
        assert writer.succeeded()

    # Create the first row.
    with SynchronousWriter() as writer:
        table.create([line1], completion_callback=writer.get_callback())
        assert writer.succeeded()

    # Create the second row.
    with SynchronousWriter() as writer:
        table.create([line2], completion_callback=writer.get_callback())
        assert writer.succeeded()

    # No effect: The primary key constraint will prevent any conflicting entry from being added.
    with pytest.raises(sqlite3.IntegrityError):
        with SynchronousWriter() as writer:
            table.create([line1], completion_callback=writer.get_callback())
            assert not writer.succeeded()

    db_lines = table.read()
    assert 2 == len(db_lines)
    db_line1 = [db_line for db_line in db_lines if db_line[0] == line1[0]][0]
    assert line1 == db_line1
    db_line2 = [db_line for db_line in db_lines if db_line[0] == line2[0]][0]
    assert line2 == db_line2

    date_updated = 20

    with SynchronousWriter() as writer:
        table.update_derivation_data([(b'234', line1[0])],
                                     date_updated,
                                     completion_callback=writer.get_callback())
        assert writer.succeeded()

    with SynchronousWriter() as writer:
        table.update_flags([(False, line2[0])],
                           date_updated,
                           completion_callback=writer.get_callback())
        assert writer.succeeded()

    db_lines = table.read()
    assert 2 == len(db_lines)
    db_line1 = [db_line for db_line in db_lines if db_line[0] == line1[0]][0]
    assert b'234' == db_line1[4]
    db_line2 = [db_line for db_line in db_lines if db_line[0] == line2[0]][0]
    assert not db_line2[6]

    with SynchronousWriter() as writer:
        table.delete([line2[0]], completion_callback=writer.get_callback())
        assert writer.succeeded()

    db_lines = table.read()
    assert 1 == len(db_lines)
    assert db_lines[0].keyinstance_id == line1.keyinstance_id
    assert db_lines[0].description is None

    # Now try out the labels.
    with SynchronousWriter() as writer:
        table.update_descriptions([("line1", line1.keyinstance_id)],
                                  completion_callback=writer.get_callback())
        assert writer.succeeded()

    rows = table.read()
    assert len(rows) == 1
    assert rows[0].keyinstance_id == line1[0]
    assert rows[0].description == "line1"
Пример #7
0
def test_table_accounts_crud(db_context: DatabaseContext) -> None:
    table = AccountTable(db_context)
    assert [] == table.read()

    table._get_current_timestamp = lambda: 10

    ACCOUNT_ID = 10
    MASTERKEY_ID = 20

    line1 = AccountRow(ACCOUNT_ID + 1, MASTERKEY_ID + 1, ScriptType.P2PKH,
                       'name1')
    line2 = AccountRow(ACCOUNT_ID + 2, MASTERKEY_ID + 1, ScriptType.P2PK,
                       'name2')

    # No effect: The masterkey foreign key constraint will fail as the masterkey does not exist.
    with pytest.raises(sqlite3.IntegrityError):
        with SynchronousWriter() as writer:
            table.create([line1], completion_callback=writer.get_callback())
            assert not writer.succeeded()

    # Satisfy the masterkey foreign key constraint by creating the masterkey.
    mktable = MasterKeyTable(db_context)
    with SynchronousWriter() as writer:
        mktable.create([MasterKeyRow(MASTERKEY_ID + 1, None, 2, b'111')],
                       completion_callback=writer.get_callback())
        assert writer.succeeded()

    # Create the first row.
    with SynchronousWriter() as writer:
        table.create([line1], completion_callback=writer.get_callback())
        assert writer.succeeded()

    # Create the second row.
    with SynchronousWriter() as writer:
        table.create([line2], completion_callback=writer.get_callback())
        assert writer.succeeded()

    # No effect: The primary key constraint will prevent any conflicting entry from being added.
    with pytest.raises(sqlite3.IntegrityError):
        with SynchronousWriter() as writer:
            table.create([line1], completion_callback=writer.get_callback())
            assert not writer.succeeded()

    db_lines = table.read()
    assert 2 == len(db_lines)
    db_line1 = [db_line for db_line in db_lines if db_line[0] == line1[0]][0]
    assert line1 == db_line1
    db_line2 = [db_line for db_line in db_lines if db_line[0] == line2[0]][0]
    assert line2 == db_line2

    date_updated = 20

    with SynchronousWriter() as writer:
        table.update_masterkey(
            [(MASTERKEY_ID + 1, ScriptType.MULTISIG_BARE, line1[0])],
            date_updated,
            completion_callback=writer.get_callback())
        assert writer.succeeded()

    with SynchronousWriter() as writer:
        table.update_name([('new_name', line2[0])],
                          date_updated,
                          completion_callback=writer.get_callback())
        assert writer.succeeded()

    db_lines = table.read()
    assert 2 == len(db_lines)
    db_line1 = [db_line for db_line in db_lines if db_line[0] == line1[0]][0]
    assert ScriptType.MULTISIG_BARE == db_line1[2]
    db_line2 = [db_line for db_line in db_lines if db_line[0] == line2[0]][0]
    assert 'new_name' == db_line2[3]

    with SynchronousWriter() as writer:
        table.delete([line2[0]], completion_callback=writer.get_callback())
        assert writer.succeeded()

    db_lines = table.read()
    assert 1 == len(db_lines)
    assert db_lines[0][0] == line1[0]
Пример #8
0
 def _create_standard_wallet(self, ks: keystore.KeyStore) -> StandardAccount:
     masterkey_row = self.wallet.create_masterkey_from_keystore(ks)
     account_row = AccountRow(1, masterkey_row.masterkey_id, ScriptType.P2PKH, '...')
     account = StandardAccount(self.wallet, account_row, [], [])
     account.synchronize()
     return account
Пример #9
0
def test_key_archive_unarchive(mocker) -> None:
    state = MockAppState()
    mocker.patch.object(state, "async_", return_value=NotImplemented)

    mock_prt = mocker.patch("electrumsv.wallet_database.tables.PaymentRequestTable.read")
    mock_prt.return_value = []

    mock_tdt = mocker.patch(
        "electrumsv.wallet_database.tables.TransactionDeltaTable.update_used_keys")
    mock_tdt.return_value = [ KEYINSTANCE_ID+1 ]

    account_row = AccountRow(ACCOUNT_ID, MASTERKEY_ID, ScriptType.P2PKH, "ACCOUNT 1")
    keyinstance_rows = [
        KeyInstanceRow(KEYINSTANCE_ID+1, ACCOUNT_ID, MASTERKEY_ID, DerivationType.BIP32,
            b'111', ScriptType.P2PKH, KeyInstanceFlag.IS_ACTIVE, None),
        KeyInstanceRow(KEYINSTANCE_ID+2, ACCOUNT_ID, MASTERKEY_ID, DerivationType.BIP32,
            b'111', ScriptType.P2PKH, KeyInstanceFlag.IS_ACTIVE, None),
        KeyInstanceRow(KEYINSTANCE_ID+3, ACCOUNT_ID, MASTERKEY_ID, DerivationType.BIP32,
            b'111', ScriptType.P2PKH, KeyInstanceFlag.IS_ACTIVE, None),
    ]
    transactionoutput_rows = [
        TransactionOutputRow(TX_HASH_1, 1, 100, KEYINSTANCE_ID+1, TransactionOutputFlag.IS_SPENT),
        TransactionOutputRow(TX_HASH_1, 2, 100, KEYINSTANCE_ID+2, TransactionOutputFlag.IS_SPENT),
        TransactionOutputRow(TX_HASH_1, 3, 200, KEYINSTANCE_ID+3, TransactionOutputFlag.NONE),
    ]

    wallet = MockWallet()
    account = CustomAccount(wallet, account_row, keyinstance_rows, transactionoutput_rows)

    assert (TX_HASH_1, 1) in account._stxos
    assert KEYINSTANCE_ID + 1 in account._keyinstances
    assert (TX_HASH_1, 2) in account._stxos
    assert KEYINSTANCE_ID + 2 in account._keyinstances
    assert (TX_HASH_1, 3) in account._utxos
    assert KEYINSTANCE_ID + 3 in account._keyinstances

    ## TEST ARCHIVE

    # Verify that the database updates are correct.
    def check_update_keyinstance_flags1(entries: List[Tuple[KeyInstanceFlag, int]]) -> None:
        for entry in entries:
            assert entry[1] == KEYINSTANCE_ID + 1
            assert entry[0] & KeyInstanceFlag.IS_ACTIVE == 0
    wallet.update_keyinstance_flags = check_update_keyinstance_flags1

    result = account.archive_keys({ KEYINSTANCE_ID+1 })

    # Verify the return value is correct.
    assert 1 == len(result)
    assert { KEYINSTANCE_ID + 1 } == result

    # Verify that the wallet state is removed.
    assert (TX_HASH_1, 1) not in account._stxos
    assert KEYINSTANCE_ID + 1 not in account._keyinstances

    ## TEST UNARCHIVE

    def fake_read_transactionoutputs(mask: Optional[TransactionOutputFlag]=None,
            key_ids: Optional[List[int]]=None) -> List[TransactionOutputRow]:
        return [ transactionoutput_rows[0] ]
    wallet.read_transactionoutputs = fake_read_transactionoutputs

    def fake_read_keyinstances(mask: Optional[KeyInstanceFlag]=None,
            key_ids: Optional[List[int]]=None) -> List[KeyInstanceRow]:
        return [
            KeyInstanceRow(KEYINSTANCE_ID+1, ACCOUNT_ID, MASTERKEY_ID, DerivationType.BIP32,
                b'111', ScriptType.P2PKH, KeyInstanceFlag.NONE, None)
        ]
    wallet.read_keyinstances = fake_read_keyinstances

    # Verify that the database updates are correct.
    def check_update_keyinstance_flags2(entries: List[Tuple[KeyInstanceFlag, int]]) -> None:
        for entry in entries:
            assert entry[1] == KEYINSTANCE_ID + 1
            assert entry[0] & KeyInstanceFlag.IS_ACTIVE == KeyInstanceFlag.IS_ACTIVE
    wallet.update_keyinstance_flags = check_update_keyinstance_flags2

    account.unarchive_transaction_keys([ (TX_HASH_1, { KEYINSTANCE_ID + 1 }) ])

    # Verify that the wallet state is restored.
    assert (TX_HASH_1, 1) in account._stxos
    assert KEYINSTANCE_ID + 1 in account._keyinstances