|
|
@ -26,11 +26,9 @@ import asyncio |
|
|
|
import itertools |
|
|
|
from collections import defaultdict |
|
|
|
|
|
|
|
from aiorpcx import TaskGroup |
|
|
|
|
|
|
|
from . import bitcoin |
|
|
|
from .bitcoin import COINBASE_MATURITY, TYPE_ADDRESS, TYPE_PUBKEY |
|
|
|
from .util import PrintError, profiler, bfh, VerifiedTxInfo, TxMinedStatus |
|
|
|
from .util import PrintError, profiler, bfh, VerifiedTxInfo, TxMinedStatus, aiosafe, CustomTaskGroup |
|
|
|
from .transaction import Transaction, TxOutput |
|
|
|
from .synchronizer import Synchronizer |
|
|
|
from .verifier import SPV |
|
|
@ -62,6 +60,7 @@ class AddressSynchronizer(PrintError): |
|
|
|
self.synchronizer = None |
|
|
|
self.verifier = None |
|
|
|
self.sync_restart_lock = asyncio.Lock() |
|
|
|
self.group = None |
|
|
|
# locks: if you need to take multiple ones, acquire them in the order they are defined here! |
|
|
|
self.lock = threading.RLock() |
|
|
|
self.transaction_lock = threading.RLock() |
|
|
@ -138,34 +137,45 @@ class AddressSynchronizer(PrintError): |
|
|
|
# add it in case it was previously unconfirmed |
|
|
|
self.add_unverified_tx(tx_hash, tx_height) |
|
|
|
|
|
|
|
async def on_default_server_changed(self, evt): |
|
|
|
@aiosafe |
|
|
|
async def on_default_server_changed(self, event): |
|
|
|
async with self.sync_restart_lock: |
|
|
|
interface = self.network.interface |
|
|
|
if interface is None: |
|
|
|
return # we should get called again soon |
|
|
|
self.verifier = SPV(self.network, self) |
|
|
|
self.synchronizer = Synchronizer(self) |
|
|
|
await interface.group.spawn(self.verifier.main(interface)) |
|
|
|
await interface.group.spawn(self.synchronizer.send_subscriptions(interface)) |
|
|
|
await interface.group.spawn(self.synchronizer.handle_status(interface)) |
|
|
|
await interface.group.spawn(self.synchronizer.main()) |
|
|
|
self.stop_threads() |
|
|
|
await self._start_threads() |
|
|
|
|
|
|
|
def start_threads(self, network): |
|
|
|
def start_network(self, network): |
|
|
|
self.network = network |
|
|
|
if self.network is not None: |
|
|
|
self.network.register_callback(self.on_default_server_changed, ['default_server_changed']) |
|
|
|
self.network.trigger_callback('default_server_changed') |
|
|
|
else: |
|
|
|
self.verifier = None |
|
|
|
self.synchronizer = None |
|
|
|
asyncio.run_coroutine_threadsafe(self._start_threads(), network.asyncio_loop) |
|
|
|
|
|
|
|
async def _start_threads(self): |
|
|
|
interface = self.network.interface |
|
|
|
if interface is None: |
|
|
|
return # we should get called again soon |
|
|
|
|
|
|
|
self.verifier = SPV(self.network, self) |
|
|
|
self.synchronizer = synchronizer = Synchronizer(self) |
|
|
|
assert self.group is None, 'group already exists' |
|
|
|
self.group = CustomTaskGroup() |
|
|
|
|
|
|
|
async def job(): |
|
|
|
async with self.group as group: |
|
|
|
await group.spawn(self.verifier.main(group)) |
|
|
|
await group.spawn(self.synchronizer.send_subscriptions(group)) |
|
|
|
await group.spawn(self.synchronizer.handle_status(group)) |
|
|
|
await group.spawn(self.synchronizer.main()) |
|
|
|
# we are being cancelled now |
|
|
|
interface.session.unsubscribe(synchronizer.status_queue) |
|
|
|
await interface.group.spawn(job) |
|
|
|
|
|
|
|
def stop_threads(self): |
|
|
|
if self.network: |
|
|
|
#self.network.remove_jobs([self.verifier]) |
|
|
|
self.synchronizer = None |
|
|
|
self.verifier = None |
|
|
|
# Now no references to the synchronizer or verifier |
|
|
|
# remain so they will be GC-ed |
|
|
|
if self.group: |
|
|
|
asyncio.run_coroutine_threadsafe(self.group.cancel_remaining(), self.network.asyncio_loop) |
|
|
|
self.group = None |
|
|
|
self.storage.put('stored_height', self.get_local_height()) |
|
|
|
self.save_transactions() |
|
|
|
self.save_verified_tx() |
|
|
|