from teos.responder import TransactionTracker
from teos.extended_appointment import ExtendedAppointment
[docs]class Builder:
"""
The :class:`Builder` class is in charge of reconstructing data loaded from the appointments database and build the
data structures of the :obj:`Watcher <teos.watcher.Watcher>` and the :obj:`Responder <teos.responder.Responder>`.
"""
[docs] @staticmethod
def build_appointments(appointments_data):
"""
Builds an appointments dictionary (``uuid:extended_appointment``) and a locator_uuid_map (``locator:uuid``)
given a dictionary of appointments from the database.
Args:
appointments_data (:obj:`dict`): a dictionary of dictionaries representing all the
:obj:`Watcher <teos.watcher.Watcher>` appointments stored in the database. The structure is as follows:
``{uuid: {locator: str, ...}, uuid: {locator:...}}``
Returns:
:obj:`tuple`: A tuple with two dictionaries. ``appointments`` containing the appointment information in
:obj:`ExtendedAppointment <teos.extended_appointment.ExtendedAppointment>` objects and ``locator_uuid_map``
containing a map of appointment (``uuid:locator``).
"""
appointments = {}
locator_uuid_map = {}
for uuid, data in appointments_data.items():
appointment = ExtendedAppointment.from_dict(data)
appointments[uuid] = appointment.get_summary()
if appointment.locator in locator_uuid_map:
locator_uuid_map[appointment.locator].append(uuid)
else:
locator_uuid_map[appointment.locator] = [uuid]
return appointments, locator_uuid_map
[docs] @staticmethod
def build_trackers(tracker_data):
"""
Builds a tracker dictionary (``uuid:TransactionTracker``) and a tx_tracker_map (``penalty_txid:uuid``) given
a dictionary of trackers from the database.
Args:
tracker_data (:obj:`dict`): a dictionary of dictionaries representing all the
:mod:`Responder <teos.responder.Responder>` trackers stored in the database.
The structure is as follows:
``{uuid: {locator: str, dispute_txid: str, ...}, uuid: {locator:...}}``
Returns:
:obj:`tuple`: A tuple with two dictionaries. ``trackers`` containing the trackers' information in
:obj:`TransactionTracker <teos.responder.TransactionTracker>` objects and a ``tx_tracker_map`` containing
the map of trackers (``penalty_txid: uuid``).
"""
trackers = {}
tx_tracker_map = {}
for uuid, data in tracker_data.items():
tracker = TransactionTracker.from_dict(data)
trackers[uuid] = tracker.get_summary()
if tracker.penalty_txid in tx_tracker_map:
tx_tracker_map[tracker.penalty_txid].append(uuid)
else:
tx_tracker_map[tracker.penalty_txid] = [uuid]
return trackers, tx_tracker_map
[docs] @staticmethod
def populate_block_queue(block_queue, missed_blocks):
"""
Populates a ``Queue`` of block hashes to initialize the :mod:`Watcher <teos.watcher.Watcher>` or the
:mod:`Responder <teos.responder.Responder>` using backed up data.
Args:
block_queue (:obj:`Queue`): a queue.
missed_blocks (:obj:`list`): list of block hashes missed by the Watchtower (due to a crash or shutdown).
Returns:
:obj:`Queue`: A queue containing all the missed blocks hashes.
"""
for block in missed_blocks:
block_queue.put(block)
[docs] @staticmethod
def update_states(watcher, missed_blocks_watcher, missed_blocks_responder):
"""
Updates the states of both the :mod:`Watcher <teos.watcher.Watcher>` and the
:mod:`Responder <teos.responder.Responder>`. If both have pending blocks to process they need to be updated at
the same time, block by block.
If only one instance has to be updated, ``populate_block_queue`` should be used.
Args:
watcher (:obj:`Watcher <teos.watcher.Watcher>`): a :obj:`Watcher` instance (including a :obj:`Responder`).
missed_blocks_watcher (:obj:`list`): the list of block missed by the :obj:`Watcher`.
missed_blocks_responder (:obj:`list`): the list of block missed by the :obj:`Responder`.
Raises:
ValueError: if one of the provided list is empty.
"""
if len(missed_blocks_responder) == 0 or len(missed_blocks_watcher) == 0:
raise ValueError(
"Both the Watcher and the Responder must have missed blocks. Use ``populate_block_queue`` otherwise."
)
# If the missed blocks of the Watcher and the Responder are not the same, we need to bring one up to date with
# the other.
if len(missed_blocks_responder) > len(missed_blocks_watcher):
block_diff = sorted(
set(missed_blocks_responder).difference(missed_blocks_watcher), key=missed_blocks_responder.index
)
Builder.populate_block_queue(watcher.responder.block_queue, block_diff)
watcher.responder.block_queue.join()
elif len(missed_blocks_watcher) > len(missed_blocks_responder):
block_diff = sorted(
set(missed_blocks_watcher).difference(missed_blocks_responder), key=missed_blocks_watcher.index
)
Builder.populate_block_queue(watcher.block_queue, block_diff)
watcher.block_queue.join()
# Once they are at the same height, we update them one by one
for block in missed_blocks_watcher:
watcher.block_queue.put(block)
watcher.block_queue.join()
watcher.responder.block_queue.put(block)
watcher.responder.block_queue.join()