Beispiel #1
0
 def __init__(self):
     self._id = 1
     self._wallet = MockWallet()
     self._deactivated_keys_lock = threading.Lock()
     self._deactivated_keys = []
     self._keyinstances = {
         1:
         KeyInstanceRow(1, 1, 1, DerivationType.BIP32,
                        json.dumps({"subpath": [0,
                                                1]}), ScriptType.P2PKH,
                        KeyInstanceFlag.IS_ACTIVE, ""),
         2:
         KeyInstanceRow(2, 1, 1, DerivationType.BIP32,
                        json.dumps({"subpath": [0,
                                                2]}), ScriptType.P2PKH,
                        KeyInstanceFlag.USER_SET_ACTIVE, ""),
         3:
         KeyInstanceRow(3, 1, 1, DerivationType.BIP32,
                        json.dumps({"subpath": [0,
                                                3]}), ScriptType.P2PKH,
                        (KeyInstanceFlag.IS_ACTIVE
                         | KeyInstanceFlag.USER_SET_ACTIVE), "")
     }
     self._deactivated_keys_event = asyncio.Event()
     self._logger = logging.getLogger("MockAccount")
Beispiel #2
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)
Beispiel #3
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"
Beispiel #4
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
Beispiel #5
0
 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)
     ]