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