electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 8e89c0c971645c11e205a163b38e4b35fa3e85de
parent d81110014e5a1d32ff424c7a325c7283cd895732
Author: SomberNight <somber.night@protonmail.com>
Date:   Sat,  7 Dec 2019 05:42:28 +0100

wallet: some clean-up re get_address_history vs db.get_addr_history

note: tests needed changing due to behavioural change in wallet.get_receiving_address()
Previously wallet.get_receiving_address used wallet.db.get_addr_history,
now it (indirectly) uses wallet.get_address_history, which now also considers local txns.

Diffstat:
Melectrum/address_synchronizer.py | 17+++++++++++------
Melectrum/tests/test_wallet_vertical.py | 26+++++++++++++-------------
Melectrum/wallet.py | 17++++++++---------
3 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py @@ -110,7 +110,13 @@ class AddressSynchronizer(Logger): def get_addresses(self): return sorted(self.db.get_history()) - def get_address_history(self, addr): + def get_address_history(self, addr: str) -> Sequence[Tuple[str, int]]: + """Returns the history for the address, in the format that would be returned by a server. + + Note: The difference between db.get_addr_history and this method is that + db.get_addr_history stores the response from a server, so it only includes txns + a server sees, i.e. that does not contain local and future txns. + """ h = [] # we need self.transaction_lock but get_tx_height will take self.lock # so we need to take that too here, to enforce order of locks @@ -378,7 +384,7 @@ class AddressSynchronizer(Logger): @profiler def load_local_history(self): - self._history_local = {} # address -> set(txid) + self._history_local = {} # type: Dict[str, Set[str]] # address -> set(txid) self._address_history_changed_events = defaultdict(asyncio.Event) # address -> Event for txid in itertools.chain(self.db.list_txi(), self.db.list_txo()): self._add_tx_to_local_history(txid) @@ -841,11 +847,10 @@ class AddressSynchronizer(Logger): xx += x return cc, uu, xx - def is_used(self, address): - h = self.db.get_addr_history(address) - return len(h) != 0 + def is_used(self, address: str) -> bool: + return self.get_address_history_len(address) != 0 - def is_empty(self, address): + def is_empty(self, address: str) -> bool: c, u, x = self.get_addr_balance(address) return c+u+x == 0 diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py @@ -603,10 +603,10 @@ class TestWalletSending(TestCaseForTestnet): tx_copy = tx_from_any(tx.serialize()) self.assertTrue(wallet2.is_mine(wallet2.get_txin_address(tx_copy.inputs()[0]))) - self.assertEqual('0100000001e228327e4c0bb80661d258d625f516307e7c127c7f3e2b476a22e89b4dae063c000000006a47304402207d352950df94d1243917abe4fa5c4f74e0ad7eb47ee02232a2939f52d87d6e57022047603b9ffb32a200d530f5c54eed5245f1034e6ed77462d23e3417b8e763f25c0121030b482838721a38d94847699fed8818b5c5f56500ef72f13489e365b65e5749cffeffffff02a0860100000000001600148a28bddb7f61864bdcf58b2ad13d5aeb3abc3c4268360200000000001976a914ca4c60999c46c2108326590b125aefd476dcb11888ac00000000', + self.assertEqual('0100000001e228327e4c0bb80661d258d625f516307e7c127c7f3e2b476a22e89b4dae063c000000006a47304402200c7b06ff882db5ffe9d6e2a3cc2cabf5cd1b4224f1453d1e3dadd13b3d391e2c02201d23fde8482b05837f27d43021d17a1be2ee619dfc889ee80d4c2761e7c7ffb20121030b482838721a38d94847699fed8818b5c5f56500ef72f13489e365b65e5749cffeffffff02a086010000000000160014284520c815980d426264766d8d930013dd20aa6068360200000000001976a914ca4c60999c46c2108326590b125aefd476dcb11888ac00000000', str(tx_copy)) - self.assertEqual('d0e40c8a8586f7faf33505269df64ebad317e8188f008649faaf48cbeb49dae8', tx_copy.txid()) - self.assertEqual('d0e40c8a8586f7faf33505269df64ebad317e8188f008649faaf48cbeb49dae8', tx_copy.wtxid()) + self.assertEqual('4ff22c31dd884dedbb905fae275508d1f7bb4948c1c979d2567132848fdff24a', tx_copy.txid()) + self.assertEqual('4ff22c31dd884dedbb905fae275508d1f7bb4948c1c979d2567132848fdff24a', tx_copy.wtxid()) self.assertEqual(tx.wtxid(), tx_copy.wtxid()) wallet1.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED) @@ -684,10 +684,10 @@ class TestWalletSending(TestCaseForTestnet): tx_copy = tx_from_any(tx.serialize()) self.assertTrue(wallet2.is_mine(wallet2.get_txin_address(tx_copy.inputs()[0]))) - self.assertEqual('01000000011e83f7d0783543a4fbbb20636bcbc7fbdf04392a5b8aa1d2551e180819ee08b5000000008a47304402205cfbf0925018883e4d1ca4168443646669ab37eeb39943fbf10b02954e00022c02201208806b63a88b42d67eb89afec3aa6b959f793fcf77c63d85082e477d74cbe50141045f7ba332df2a7b4f5d13f246e307c9174cfa9b8b05f3b83410a3c23ef8958d610be285963d67c7bc1feb082f168fa9877c25999963ff8b56b242a852b23e25edfeffffff02a08601000000000017a91480c2353f6a7bc3c71e99e062655b19adb3dd2e4887280b0400000000001976a914ca14915184a2662b5d1505ce7142c8ca066c70e288ac00000000', + self.assertEqual('01000000011e83f7d0783543a4fbbb20636bcbc7fbdf04392a5b8aa1d2551e180819ee08b5000000008a473044022007569f938b5d7a7f529ceccc413363d84325c11d589c1897660bebfd5fd1cc4302203ef71fa42f9b31bb1e816af13b0bf725c493a0405433390c783cd9374713c5880141045f7ba332df2a7b4f5d13f246e307c9174cfa9b8b05f3b83410a3c23ef8958d610be285963d67c7bc1feb082f168fa9877c25999963ff8b56b242a852b23e25edfeffffff02a08601000000000017a914efe136b8275f49bc0f9871eebb9a48d0516229fd87280b0400000000001976a914ca14915184a2662b5d1505ce7142c8ca066c70e288ac00000000', str(tx_copy)) - self.assertEqual('a306dcfce59813b572c811cf842fe8a5de23afb8d187a0f7e10e28726e2cf798', tx_copy.txid()) - self.assertEqual('a306dcfce59813b572c811cf842fe8a5de23afb8d187a0f7e10e28726e2cf798', tx_copy.wtxid()) + self.assertEqual('30f6eec4db5e6b1dfe572dfbc7077661df9a15a2a1b7701612b906d3e1bee3d8', tx_copy.txid()) + self.assertEqual('30f6eec4db5e6b1dfe572dfbc7077661df9a15a2a1b7701612b906d3e1bee3d8', tx_copy.wtxid()) self.assertEqual(tx.wtxid(), tx_copy.wtxid()) wallet1a.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED) @@ -779,7 +779,7 @@ class TestWalletSending(TestCaseForTestnet): tx = wallet2a.mktx(outputs=outputs, password=None, fee=5000, tx_version=1) txid = tx.txid() partial_tx = tx.serialize_as_bytes().hex() - self.assertEqual("70736274ff01007e010000000149d077be0ee9d52776211e9b4fec1cc02bd53661a04e120a97db8b78d83c9c6e0100000000feffffff0260ea00000000000017a9143025051b6b5ccd4baf30dfe2de8aa84f0dd567ed87a0860100000000002200203c43ac80d6e3015cf378bf6bac0c22456723d6050bef324ec641e7762440c63c0000000000010120888402000000000017a914187842cea9c15989a51ce7ca889a08b824bf874387220202119f899075a131d4d519d4cdcf5de5907dc2df3b93d54b53ded852211d2b6cb1473044022021f11caa2508f44cf2b2f5d67dab26d8f35a8e6f64aa32410a764dc4dab0d7b2022060be82ac3715634f272dcc62e82acde61b7663da675d1077718d6fec5619b2000101042200204311edae835c7a5aa712c8ca644180f13a3b2f3b420fa879b181474724d6163c010547522102119f899075a131d4d519d4cdcf5de5907dc2df3b93d54b53ded852211d2b6cb12102fdb0f6775d4b6619257c43343ba5e7807b0164f1eb3f00f2b594ab9e53ab812652ae220602119f899075a131d4d519d4cdcf5de5907dc2df3b93d54b53ded852211d2b6cb10cd1dbcc210000000000000000220602fdb0f6775d4b6619257c43343ba5e7807b0164f1eb3f00f2b594ab9e53ab81260c17cea9140000000000000000000100220020717ab7037b81797cb3e192a8a1b4d88083444bbfcd26934cadf3bcf890f14e05010147522102987c184fcd8ace2e2a314250e04a15a4b8c885fb4eb778ab82c45838bcbcbdde21034084c4a0493c248783e60d8415cd30b3ba2c3b7a79201e38b953adea2bc44f9952ae220202987c184fcd8ace2e2a314250e04a15a4b8c885fb4eb778ab82c45838bcbcbdde0c17cea91401000000000000002202034084c4a0493c248783e60d8415cd30b3ba2c3b7a79201e38b953adea2bc44f990cd1dbcc2101000000000000000000", + self.assertEqual("70736274ff01007e010000000149d077be0ee9d52776211e9b4fec1cc02bd53661a04e120a97db8b78d83c9c6e0100000000feffffff0260ea00000000000017a9143025051b6b5ccd4baf30dfe2de8aa84f0dd567ed87a086010000000000220020f7b6b30c3073ae2680a7e90c589bbfec5303331be68bbab843eed5d51ba012390000000000010120888402000000000017a914187842cea9c15989a51ce7ca889a08b824bf874387220202119f899075a131d4d519d4cdcf5de5907dc2df3b93d54b53ded852211d2b6cb14730440220091ea67af7c1131f51f62fe9596dff0a60c8b45bfc5be675389e193912e8a71802201bf813bbf83933a35ecc46e2d5b0442bd8758fa82e0f8ed16392c10d51f7f7660101042200204311edae835c7a5aa712c8ca644180f13a3b2f3b420fa879b181474724d6163c010547522102119f899075a131d4d519d4cdcf5de5907dc2df3b93d54b53ded852211d2b6cb12102fdb0f6775d4b6619257c43343ba5e7807b0164f1eb3f00f2b594ab9e53ab812652ae220602119f899075a131d4d519d4cdcf5de5907dc2df3b93d54b53ded852211d2b6cb10cd1dbcc210000000000000000220602fdb0f6775d4b6619257c43343ba5e7807b0164f1eb3f00f2b594ab9e53ab81260c17cea9140000000000000000000100220020717ab7037b81797cb3e192a8a1b4d88083444bbfcd26934cadf3bcf890f14e05010147522102987c184fcd8ace2e2a314250e04a15a4b8c885fb4eb778ab82c45838bcbcbdde21034084c4a0493c248783e60d8415cd30b3ba2c3b7a79201e38b953adea2bc44f9952ae220202987c184fcd8ace2e2a314250e04a15a4b8c885fb4eb778ab82c45838bcbcbdde0c17cea91401000000000000002202034084c4a0493c248783e60d8415cd30b3ba2c3b7a79201e38b953adea2bc44f990cd1dbcc2101000000000000000000", partial_tx) tx = tx_from_any(partial_tx) # simulates moving partial txn between cosigners self.assertEqual(txid, tx.txid()) @@ -793,10 +793,10 @@ class TestWalletSending(TestCaseForTestnet): tx_copy = tx_from_any(tx.serialize()) self.assertTrue(wallet2a.is_mine(wallet2a.get_txin_address(tx_copy.inputs()[0]))) - self.assertEqual('0100000000010149d077be0ee9d52776211e9b4fec1cc02bd53661a04e120a97db8b78d83c9c6e01000000232200204311edae835c7a5aa712c8ca644180f13a3b2f3b420fa879b181474724d6163cfeffffff0260ea00000000000017a9143025051b6b5ccd4baf30dfe2de8aa84f0dd567ed87a0860100000000002200203c43ac80d6e3015cf378bf6bac0c22456723d6050bef324ec641e7762440c63c0400473044022021f11caa2508f44cf2b2f5d67dab26d8f35a8e6f64aa32410a764dc4dab0d7b2022060be82ac3715634f272dcc62e82acde61b7663da675d1077718d6fec5619b2000147304402204af5edbab2d674f6a9edef8c97b2f7fdf8ababedc7b287710cc7a64d4699358b022064e2d07f4bb32373be31b2003dc56b7b831a7c01419326efb3011c64b898b3f00147522102119f899075a131d4d519d4cdcf5de5907dc2df3b93d54b53ded852211d2b6cb12102fdb0f6775d4b6619257c43343ba5e7807b0164f1eb3f00f2b594ab9e53ab812652ae00000000', + self.assertEqual('0100000000010149d077be0ee9d52776211e9b4fec1cc02bd53661a04e120a97db8b78d83c9c6e01000000232200204311edae835c7a5aa712c8ca644180f13a3b2f3b420fa879b181474724d6163cfeffffff0260ea00000000000017a9143025051b6b5ccd4baf30dfe2de8aa84f0dd567ed87a086010000000000220020f7b6b30c3073ae2680a7e90c589bbfec5303331be68bbab843eed5d51ba0123904004730440220091ea67af7c1131f51f62fe9596dff0a60c8b45bfc5be675389e193912e8a71802201bf813bbf83933a35ecc46e2d5b0442bd8758fa82e0f8ed16392c10d51f7f7660147304402203ecf75b0316a449dd31bc549251b687dc904194aa551941bd5e8c67603661bdb02204ed58b3a6b070ec138d2127093bebcc6581495818fa611583e1c81cd9b2cf5ee0147522102119f899075a131d4d519d4cdcf5de5907dc2df3b93d54b53ded852211d2b6cb12102fdb0f6775d4b6619257c43343ba5e7807b0164f1eb3f00f2b594ab9e53ab812652ae00000000', str(tx_copy)) - self.assertEqual('84b0dcb43022385f7a10e2710e5625a2be3cd6e390387b6100b55500d5eea8f6', tx_copy.txid()) - self.assertEqual('5384a9f8d8aaa38f35a9f6705add54bb88ce5cf54ad0f60ec997d31977271e78', tx_copy.wtxid()) + self.assertEqual('df92f0179b2bd4d0845472a8492edcaa3c24883ec4c7816dcd634183e0f89f29', tx_copy.txid()) + self.assertEqual('614a3c2d908229e5421364b5ac9802eb4636ead08c080cae3c7ca6ba4ad5f3cf', tx_copy.wtxid()) self.assertEqual(tx.wtxid(), tx_copy.wtxid()) self.assertEqual(txid, tx_copy.txid()) @@ -864,10 +864,10 @@ class TestWalletSending(TestCaseForTestnet): tx_copy = tx_from_any(tx.serialize()) self.assertTrue(wallet2.is_mine(wallet2.get_txin_address(tx_copy.inputs()[0]))) - self.assertEqual('01000000000101e6f9dffd569129216d4d4fcb2ea98a623e40d7c615de0943a867f9f8b6d6494600000000171600149fad840ed174584ee054bd26f3e411817338c5edfeffffff02e09304000000000017a914b0b9f31bace76cdfae2c14abc03e223403d7dc4b87d89a0a000000000017a9148ccd0efb2be5b412c4033715f560ed8f446c8ceb8702473044022009e4a5a8848735fecc72d4042c4dcfea96313546f0f49432d704788ed3acfe090220389ad06ef4f4fb2eaf66f5a073cebf717aef1e6c2d11ec754f15a47f25dde88b0121038362bbf0b4918b37e9d7c75930ed3a78e3d445724cb5c37ade4a59b6e411fe4e00000000', + self.assertEqual('01000000000101e6f9dffd569129216d4d4fcb2ea98a623e40d7c615de0943a867f9f8b6d6494600000000171600149fad840ed174584ee054bd26f3e411817338c5edfeffffff02e09304000000000017a9145ae3933a6e13100f301f23227b98b0bdb5d16b8487d89a0a000000000017a9148ccd0efb2be5b412c4033715f560ed8f446c8ceb8702473044022020a3c46886b72f4ec561c5983a789098202307eae9679ff74fcb0879f65fff1d0220242ec3bfa747c513ef31874670d9c68ad235892588be55564696dd6690952e5a0121038362bbf0b4918b37e9d7c75930ed3a78e3d445724cb5c37ade4a59b6e411fe4e00000000', str(tx_copy)) - self.assertEqual('6bca882435d012a111612a6d1e09b7130127517948b24517078757e26ce1bee1', tx_copy.txid()) - self.assertEqual('9c53474159d89a831ca13c5529076b1ed142b9548038fec3ffc71bf8a8f2a888', tx_copy.wtxid()) + self.assertEqual('ae5dcacdf9e3067e18fcfd33582c24f60f844730e7872049bb627796929879ee', tx_copy.txid()) + self.assertEqual('f70bce6418fc44dcab41cbd466086aea54283821487189e4d15c4d1e2d1e267d', tx_copy.wtxid()) self.assertEqual(tx.wtxid(), tx_copy.wtxid()) wallet1a.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED) diff --git a/electrum/wallet.py b/electrum/wallet.py @@ -347,8 +347,7 @@ class Abstract_Wallet(AddressSynchronizer): addrs = self._unused_change_addresses else: addrs = self.get_change_addresses() - self._unused_change_addresses = [addr for addr in addrs if - self.get_address_history_len(addr) == 0] + self._unused_change_addresses = [addr for addr in addrs if not self.is_used(addr)] return list(self._unused_change_addresses) def is_deterministic(self): @@ -1453,14 +1452,14 @@ class Abstract_Wallet(AddressSynchronizer): return addr return wrapper - def get_unused_addresses(self): + def get_unused_addresses(self) -> Sequence[str]: domain = self.get_receiving_addresses() - in_use = [k for k in self.receive_requests.keys() if self.get_request_status(k)[0] != PR_EXPIRED] - return [addr for addr in domain if not self.db.get_addr_history(addr) - and addr not in in_use] + in_use_by_request = [k for k in self.receive_requests.keys() if self.get_request_status(k)[0] != PR_EXPIRED] + return [addr for addr in domain if not self.is_used(addr) + and addr not in in_use_by_request] @check_returned_address - def get_unused_address(self): + def get_unused_address(self) -> Optional[str]: addrs = self.get_unused_addresses() if addrs: return addrs[0] @@ -1473,7 +1472,7 @@ class Abstract_Wallet(AddressSynchronizer): return choice = domain[0] for addr in domain: - if not self.db.get_addr_history(addr): + if not self.is_used(addr): if addr not in self.receive_requests.keys(): return addr else: @@ -1920,7 +1919,7 @@ class Imported_Wallet(Simple_Wallet): transactions_new = set() # txs that are not only referred to by address with self.lock: for addr in self.db.get_history(): - details = self.db.get_addr_history(addr) + details = self.get_address_history(addr) if addr == address: for tx_hash, height in details: transactions_to_remove.add(tx_hash)