Browse Source

json_db: make get operations threadsafe

sqlite_db
ThomasV 6 years ago
parent
commit
1e519f2dd0
  1. 8
      electrum/address_synchronizer.py
  2. 48
      electrum/json_db.py

8
electrum/address_synchronizer.py

@ -292,7 +292,7 @@ class AddressSynchronizer(PrintError):
self.db.remove_spent_outpoint(prevout_hash, prevout_n) self.db.remove_spent_outpoint(prevout_hash, prevout_n)
else: else:
# expensive but always works # expensive but always works
for prevout_hash, prevout_n in list(self.db.list_spent_outpoints()): for prevout_hash, prevout_n in self.db.list_spent_outpoints():
spending_txid = self.db.get_spent_outpoint(prevout_hash, prevout_n) spending_txid = self.db.get_spent_outpoint(prevout_hash, prevout_n)
if spending_txid == tx_hash: if spending_txid == tx_hash:
self.db.remove_spent_outpoint(prevout_hash, prevout_n) self.db.remove_spent_outpoint(prevout_hash, prevout_n)
@ -348,7 +348,7 @@ class AddressSynchronizer(PrintError):
def load_local_history(self): def load_local_history(self):
self._history_local = {} # address -> set(txid) self._history_local = {} # address -> set(txid)
self._address_history_changed_events = defaultdict(asyncio.Event) # address -> Event self._address_history_changed_events = defaultdict(asyncio.Event) # address -> Event
for txid in itertools.chain(self.db.get_txi_keys(), self.db.get_txo_keys()): for txid in itertools.chain(self.db.list_txi(), self.db.list_txo()):
self._add_tx_to_local_history(txid) self._add_tx_to_local_history(txid)
@profiler @profiler
@ -372,7 +372,7 @@ class AddressSynchronizer(PrintError):
self.storage.write() self.storage.write()
def remove_local_transactions_we_dont_have(self): def remove_local_transactions_we_dont_have(self):
for txid in itertools.chain(list(self.db.get_txi_keys()), list(self.db.get_txo_keys())): for txid in itertools.chain(self.db.list_txi(), self.db.list_txo()):
tx_height = self.get_tx_height(txid).height tx_height = self.get_tx_height(txid).height
if tx_height == TX_HEIGHT_LOCAL and txid not in self.db.list_transactions(): if tx_height == TX_HEIGHT_LOCAL and txid not in self.db.list_transactions():
self.remove_transaction(txid) self.remove_transaction(txid)
@ -519,7 +519,7 @@ class AddressSynchronizer(PrintError):
'''Used by the verifier when a reorg has happened''' '''Used by the verifier when a reorg has happened'''
txs = set() txs = set()
with self.lock: with self.lock:
for tx_hash in list(self.db.list_verified_tx()): for tx_hash in self.db.list_verified_tx():
info = self.db.get_verified_tx(tx_hash) info = self.db.get_verified_tx(tx_hash)
tx_height = info.height tx_height = info.height
if tx_height >= height: if tx_height >= height:

48
electrum/json_db.py

@ -70,6 +70,13 @@ class JsonDB(PrintError):
return func(self, *args, **kwargs) return func(self, *args, **kwargs)
return wrapper return wrapper
def locked(func):
def wrapper(self, *args, **kwargs):
with self.lock:
return func(self, *args, **kwargs)
return wrapper
@locked
def get(self, key, default=None): def get(self, key, default=None):
v = self.data.get(key) v = self.data.get(key)
if v is None: if v is None:
@ -443,7 +450,6 @@ class JsonDB(PrintError):
def convert_account(self): def convert_account(self):
if not self._is_upgrade_method_needed(0, 13): if not self._is_upgrade_method_needed(0, 13):
return return
self.put('accounts', None) self.put('accounts', None)
def _is_upgrade_method_needed(self, min_version, max_version): def _is_upgrade_method_needed(self, min_version, max_version):
@ -457,6 +463,7 @@ class JsonDB(PrintError):
else: else:
return True return True
@locked
def get_seed_version(self): def get_seed_version(self):
seed_version = self.get('seed_version') seed_version = self.get('seed_version')
if not seed_version: if not seed_version:
@ -488,16 +495,19 @@ class JsonDB(PrintError):
msg += "\nPlease open this file with Electrum 1.9.8, and move your coins to a new wallet." msg += "\nPlease open this file with Electrum 1.9.8, and move your coins to a new wallet."
raise WalletFileException(msg) raise WalletFileException(msg)
@locked
def get_txi(self, tx_hash): def get_txi(self, tx_hash):
return self.txi.get(tx_hash, {}).keys() return list(self.txi.get(tx_hash, {}).keys())
@locked
def get_txo(self, tx_hash): def get_txo(self, tx_hash):
return self.txo.get(tx_hash, {}).keys() return list(self.txo.get(tx_hash, {}).keys())
@locked
def get_txi_addr(self, tx_hash, address): def get_txi_addr(self, tx_hash, address):
return self.txi.get(tx_hash, {}).get(address, []) return self.txi.get(tx_hash, {}).get(address, [])
@locked
def get_txo_addr(self, tx_hash, address): def get_txo_addr(self, tx_hash, address):
return self.txo.get(tx_hash, {}).get(address, []) return self.txo.get(tx_hash, {}).get(address, [])
@ -521,11 +531,13 @@ class JsonDB(PrintError):
d[addr] = set() d[addr] = set()
d[addr].add((n, v, is_coinbase)) d[addr].add((n, v, is_coinbase))
def get_txi_keys(self): @locked
return self.txi.keys() def list_txi(self):
return list(self.txi.keys())
def get_txo_keys(self): @locked
return self.txo.keys() def list_txo(self):
return list(self.txo.keys())
@modifier @modifier
def remove_txi(self, tx_hash): def remove_txi(self, tx_hash):
@ -535,15 +547,18 @@ class JsonDB(PrintError):
def remove_txo(self, tx_hash): def remove_txo(self, tx_hash):
self.txo.pop(tx_hash, None) self.txo.pop(tx_hash, None)
@locked
def list_spent_outpoints(self): def list_spent_outpoints(self):
return [(h, n) return [(h, n)
for h in self.spent_outpoints.keys() for h in self.spent_outpoints.keys()
for n in self.get_spent_outpoints(h) for n in self.get_spent_outpoints(h)
] ]
@locked
def get_spent_outpoints(self, prevout_hash): def get_spent_outpoints(self, prevout_hash):
return self.spent_outpoints.get(prevout_hash, {}).keys() return list(self.spent_outpoints.get(prevout_hash, {}).keys())
@locked
def get_spent_outpoint(self, prevout_hash, prevout_n): def get_spent_outpoint(self, prevout_hash, prevout_n):
return self.spent_outpoints.get(prevout_hash, {}).get(str(prevout_n)) return self.spent_outpoints.get(prevout_hash, {}).get(str(prevout_n))
@ -567,16 +582,20 @@ class JsonDB(PrintError):
def remove_transaction(self, tx_hash): def remove_transaction(self, tx_hash):
self.transactions.pop(tx_hash, None) self.transactions.pop(tx_hash, None)
@locked
def get_transaction(self, tx_hash): def get_transaction(self, tx_hash):
tx = self.transactions.get(tx_hash) tx = self.transactions.get(tx_hash)
return Transaction(tx) if tx else None return Transaction(tx) if tx else None
@locked
def list_transactions(self): def list_transactions(self):
return self.transactions.keys() return list(self.transactions.keys())
@locked
def get_history(self): def get_history(self):
return self.history.keys() return list(self.history.keys())
@locked
def get_addr_history(self, addr): def get_addr_history(self, addr):
return self.history.get(addr, []) return self.history.get(addr, [])
@ -588,9 +607,11 @@ class JsonDB(PrintError):
def remove_addr_history(self, addr): def remove_addr_history(self, addr):
self.history.pop(addr, None) self.history.pop(addr, None)
@locked
def list_verified_tx(self): def list_verified_tx(self):
return self.verified_tx.keys() return list(self.verified_tx.keys())
@locked
def get_verified_tx(self, txid): def get_verified_tx(self, txid):
if txid not in self.verified_tx: if txid not in self.verified_tx:
return None return None
@ -613,6 +634,7 @@ class JsonDB(PrintError):
def update_tx_fees(self, d): def update_tx_fees(self, d):
return self.tx_fees.update(d) return self.tx_fees.update(d)
@locked
def get_tx_fee(self, txid): def get_tx_fee(self, txid):
return self.tx_fees.get(txid) return self.tx_fees.get(txid)
@ -620,6 +642,7 @@ class JsonDB(PrintError):
def remove_tx_fee(self, txid): def remove_tx_fee(self, txid):
self.tx_fees.pop(txid, None) self.tx_fees.pop(txid, None)
@locked
def get_data_ref(self, name): def get_data_ref(self, name):
if name not in self.data: if name not in self.data:
self.data[name] = {} self.data[name] = {}
@ -656,6 +679,7 @@ class JsonDB(PrintError):
self.print_error("removing unreferenced spent outpoint") self.print_error("removing unreferenced spent outpoint")
d.pop(prevout_n) d.pop(prevout_n)
@modifier
def clear_history(self): def clear_history(self):
self.txi.clear() self.txi.clear()
self.txo.clear() self.txo.clear()

Loading…
Cancel
Save