def test_tracker_from_dict_invalid_data(generate_dummy_tracker): tracker_dict = generate_dummy_tracker().to_dict() for value in [ "locator", "dispute_txid", "penalty_txid", "penalty_rawtx", "user_id" ]: tracker_dict_copy = deepcopy(tracker_dict) tracker_dict_copy[value] = None with pytest.raises(ValueError): TransactionTracker.from_dict(tracker_dict_copy)
def test_tracker_from_dict_invalid_data(): tracker_dict = create_dummy_tracker().to_dict() for value in ["dispute_txid", "penalty_txid", "penalty_rawtx", "user_id"]: tracker_dict_copy = deepcopy(tracker_dict) tracker_dict_copy[value] = None try: TransactionTracker.from_dict(tracker_dict_copy) assert False except ValueError: assert True
def test_get_all_appointments_responder(api, client, get_all_db_manager): # Let's reset the dbs so we can test this clean api.watcher.db_manager = get_all_db_manager api.watcher.responder.db_manager = get_all_db_manager # Check that they are wiped clean r = client.get(get_all_appointment_endpoint) assert r.status_code == HTTP_OK assert len(r.json.get("watcher_appointments")) == 0 and len(r.json.get("responder_trackers")) == 0 # Add some trackers to the Responder db tx_trackers = {} for _ in range(10): uuid = get_random_value_hex(16) tracker_data = { "locator": get_random_value_hex(16), "dispute_txid": get_random_value_hex(32), "penalty_txid": get_random_value_hex(32), "penalty_rawtx": get_random_value_hex(250), "user_id": get_random_value_hex(16), } tracker = TransactionTracker.from_dict(tracker_data) tx_trackers[uuid] = tracker.to_dict() api.watcher.responder.db_manager.store_responder_tracker(uuid, tracker.to_dict()) api.watcher.db_manager.create_triggered_appointment_flag(uuid) # Get all appointments r = client.get(get_all_appointment_endpoint) # Make sure there is not pending locator in the watcher responder_trackers = [v["locator"] for k, v in r.json["responder_trackers"].items()] local_locators = [tracker["locator"] for uuid, tracker in tx_trackers.items()] assert set(responder_trackers) == set(local_locators) assert len(r.json["watcher_appointments"]) == 0
def test_check_confirmations(responder, monkeypatch): # check_confirmations checks, given a list of transaction for a block, what of the known penalty transaction have # been confirmed. To test this we need to create a list of transactions and the state of the Responder # The responder has a list of unconfirmed transaction, let make that some of them are the ones we've received txs = [get_random_value_hex(32) for _ in range(20)] unconfirmed_txs = [get_random_value_hex(32) for _ in range(10)] txs_subset = random.sample(txs, k=10) unconfirmed_txs.extend(txs_subset) # We also need to add them to the tx_tracker_map since they would be there in normal conditions tx_tracker_map = { txid: TransactionTracker(txid[:LOCATOR_LEN_HEX], txid, None, None, None) for txid in unconfirmed_txs } # Mock the structures monkeypatch.setattr(responder, "unconfirmed_txs", unconfirmed_txs) monkeypatch.setattr(responder, "tx_tracker_map", tx_tracker_map) # Let's make sure that there are no txs with missed confirmations yet assert len(responder.missed_confirmations) == 0 # After checking confirmations the txs in txs_subset should be confirmed (not part of unconfirmed_txs anymore) # and the rest should have a missing confirmation responder.check_confirmations(txs) for tx in txs_subset: assert tx not in responder.unconfirmed_txs for tx in responder.unconfirmed_txs: assert responder.missed_confirmations[tx] == 1
def test_check_confirmations(db_manager, carrier, block_processor): responder = Responder(db_manager, carrier, block_processor) chain_monitor = ChainMonitor(Queue(), responder.block_queue, block_processor, bitcoind_feed_params) chain_monitor.monitor_chain() # check_confirmations checks, given a list of transaction for a block, what of the known penalty transaction have # been confirmed. To test this we need to create a list of transactions and the state of the responder txs = [get_random_value_hex(32) for _ in range(20)] # The responder has a list of unconfirmed transaction, let make that some of them are the ones we've received responder.unconfirmed_txs = [get_random_value_hex(32) for _ in range(10)] txs_subset = random.sample(txs, k=10) responder.unconfirmed_txs.extend(txs_subset) # We also need to add them to the tx_tracker_map since they would be there in normal conditions responder.tx_tracker_map = { txid: TransactionTracker(txid[:LOCATOR_LEN_HEX], txid, None, None, None) for txid in responder.unconfirmed_txs } # Let's make sure that there are no txs with missed confirmations yet assert len(responder.missed_confirmations) == 0 responder.check_confirmations(txs) # After checking confirmations the txs in txs_subset should be confirmed (not part of unconfirmed_txs anymore) # and the rest should have a missing confirmation for tx in txs_subset: assert tx not in responder.unconfirmed_txs for tx in responder.unconfirmed_txs: assert responder.missed_confirmations[tx] == 1
def test_get_appointment_in_responder(api, client, appointment): # Mock the appointment in the Responder tracker_data = { "locator": appointment.locator, "dispute_txid": get_random_value_hex(32), "penalty_txid": get_random_value_hex(32), "penalty_rawtx": get_random_value_hex(250), "user_id": get_random_value_hex(16), } tx_tracker = TransactionTracker.from_dict(tracker_data) uuid = hash_160("{}{}".format(appointment.locator, user_id)) api.watcher.db_manager.create_triggered_appointment_flag(uuid) api.watcher.responder.db_manager.store_responder_tracker(uuid, tx_tracker.to_dict()) # Request back the data message = "get appointment {}".format(appointment.locator) signature = Cryptographer.sign(message.encode("utf-8"), user_sk) data = {"locator": appointment.locator, "signature": signature} # Next we can request it r = client.post(get_appointment_endpoint, json=data) assert r.status_code == HTTP_OK # Check that the appointment is on the Responder assert r.json.get("status") == "dispute_responded" # Check the the sent appointment matches the received one assert tx_tracker.locator == r.json.get("locator") assert tx_tracker.dispute_txid == r.json.get("appointment").get("dispute_txid") assert tx_tracker.penalty_txid == r.json.get("appointment").get("penalty_txid") assert tx_tracker.penalty_rawtx == r.json.get("appointment").get("penalty_rawtx")
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
def set_up_trackers(db_manager, total_trackers): trackers = dict() tx_tracker_map = dict() for i in range(total_trackers): uuid = uuid4().hex # We use the same txid for penalty and dispute here, it shouldn't matter penalty_txid = get_random_value_hex(32) dispute_txid = get_random_value_hex(32) locator = dispute_txid[:LOCATOR_LEN_HEX] # Appointment data appointment = Appointment(locator, None, None) # Store the data in the database and create a flag db_manager.store_watcher_appointment(uuid, appointment.to_dict()) db_manager.create_triggered_appointment_flag(uuid) # Assign both penalty_txid and dispute_txid the same id (it shouldn't matter) tracker = TransactionTracker(locator, dispute_txid, penalty_txid, None, None) trackers[uuid] = { "locator": tracker.locator, "penalty_txid": tracker.penalty_txid } tx_tracker_map[penalty_txid] = [uuid] db_manager.store_responder_tracker(uuid, tracker.to_dict()) # Each penalty_txid can have more than one uuid assigned to it. if i % 2: uuid = uuid4().hex trackers[uuid] = { "locator": tracker.locator, "penalty_txid": tracker.penalty_txid } tx_tracker_map[penalty_txid].append(uuid) db_manager.store_responder_tracker(uuid, tracker.to_dict()) # Add them to the Watcher's db too db_manager.store_watcher_appointment(uuid, appointment.to_dict()) db_manager.create_triggered_appointment_flag(uuid) return trackers, tx_tracker_map
def test_rebroadcast(db_manager, carrier, block_processor): responder = Responder(db_manager, carrier, block_processor) chain_monitor = ChainMonitor(Queue(), responder.block_queue, block_processor, bitcoind_feed_params) chain_monitor.monitor_chain() txs_to_rebroadcast = [] # Rebroadcast calls add_response with retry=True. The tracker data is already in trackers. for i in range(20): uuid = uuid4().hex locator, dispute_txid, penalty_txid, penalty_rawtx, appointment_end = create_dummy_tracker_data( penalty_rawtx=create_dummy_transaction().hex()) tracker = TransactionTracker(locator, dispute_txid, penalty_txid, penalty_rawtx, appointment_end) responder.trackers[uuid] = { "locator": locator, "penalty_txid": penalty_txid, "appointment_end": appointment_end, } # We need to add it to the db too responder.db_manager.create_triggered_appointment_flag(uuid) responder.db_manager.store_responder_tracker(uuid, tracker.to_json()) responder.tx_tracker_map[penalty_txid] = [uuid] responder.unconfirmed_txs.append(penalty_txid) # Let's add some of the txs in the rebroadcast list if (i % 2) == 0: txs_to_rebroadcast.append(penalty_txid) # The block_hash passed to rebroadcast does not matter much now. It will in the future to deal with errors receipts = responder.rebroadcast(txs_to_rebroadcast) # All txs should have been delivered and the missed confirmation reset for txid, receipt in receipts: # Sanity check assert txid in txs_to_rebroadcast assert receipt.delivered is True assert responder.missed_confirmations[txid] == 0
def test_tracker_init(run_bitcoind): locator, dispute_txid, penalty_txid, penalty_rawtx, appointment_end = create_dummy_tracker_data( ) tracker = TransactionTracker(locator, dispute_txid, penalty_txid, penalty_rawtx, appointment_end) assert (tracker.dispute_txid == dispute_txid and tracker.penalty_txid == penalty_txid and tracker.penalty_rawtx == penalty_rawtx and tracker.appointment_end == appointment_end)
def test_tracker_init(run_bitcoind): locator, dispute_txid, penalty_txid, penalty_rawtx, user_id = create_dummy_tracker_data( ) tracker = TransactionTracker(locator, dispute_txid, penalty_txid, penalty_rawtx, user_id) assert (tracker.locator == locator and tracker.dispute_txid == dispute_txid and tracker.penalty_txid == penalty_txid and tracker.penalty_rawtx == penalty_rawtx and tracker.user_id == user_id)
def _generate_dummy_tracker(): tracker_data = dict( locator=get_random_value_hex(16), dispute_txid=get_random_value_hex(32), penalty_txid=get_random_value_hex(32), penalty_rawtx=get_random_value_hex(150), user_id="02" + get_random_value_hex(32), ) return TransactionTracker.from_dict(tracker_data)
def set_up_trackers(db_manager, total_trackers): trackers = dict() tx_tracker_map = dict() for i in range(total_trackers): uuid = uuid4().hex # We use the same txid for penalty and dispute here, it shouldn't matter penalty_txid = get_random_value_hex(32) dispute_txid = get_random_value_hex(32) locator = dispute_txid[:LOCATOR_LEN_HEX] # Assign both penalty_txid and dispute_txid the same id (it shouldn't matter) tracker = TransactionTracker(locator, dispute_txid, penalty_txid, None, None) trackers[uuid] = { "locator": tracker.locator, "penalty_txid": tracker.penalty_txid } tx_tracker_map[penalty_txid] = [uuid] db_manager.store_responder_tracker(uuid, tracker.to_json()) db_manager.create_append_locator_map(tracker.locator, uuid) # Each penalty_txid can have more than one uuid assigned to it. if i % 2: uuid = uuid4().hex trackers[uuid] = { "locator": tracker.locator, "penalty_txid": tracker.penalty_txid } tx_tracker_map[penalty_txid].append(uuid) db_manager.store_responder_tracker(uuid, tracker.to_json()) db_manager.create_append_locator_map(tracker.locator, uuid) return trackers, tx_tracker_map
def test_tracker_init(): locator = get_random_value_hex(32) dispute_txid = get_random_value_hex(32) penalty_txid = get_random_value_hex(32) penalty_tx = get_random_value_hex(200) user_id = get_random_value_hex(16) tracker = TransactionTracker(locator, dispute_txid, penalty_txid, penalty_tx, user_id) assert (tracker.locator == locator and tracker.dispute_txid == dispute_txid and tracker.penalty_txid == penalty_txid and tracker.penalty_rawtx == penalty_tx and tracker.user_id == user_id)
def test_tracker_init(): # Simple test to check that creating a Tracker works locator = get_random_value_hex(32) dispute_txid = get_random_value_hex(32) penalty_txid = get_random_value_hex(32) penalty_tx = get_random_value_hex(200) user_id = get_random_value_hex(16) tracker = TransactionTracker(locator, dispute_txid, penalty_txid, penalty_tx, user_id) assert (tracker.locator == locator and tracker.dispute_txid == dispute_txid and tracker.penalty_txid == penalty_txid and tracker.penalty_rawtx == penalty_tx and tracker.user_id == user_id)
def generate_dummy_tracker(): dispute_txid = get_random_value_hex(32) penalty_txid = get_random_value_hex(32) penalty_rawtx = get_random_value_hex(100) locator = dispute_txid[:LOCATOR_LEN_HEX] tracker_data = dict( locator=locator, dispute_txid=dispute_txid, penalty_txid=penalty_txid, penalty_rawtx=penalty_rawtx, appointment_end=100, ) return TransactionTracker.from_dict(tracker_data)
def _generate_dummy_tracker(commitment_tx=None): if not commitment_tx: commitment_tx = create_commitment_tx() decoded_commitment_tx = bitcoin_cli.decoderawtransaction(commitment_tx) penalty_tx = create_penalty_tx(decoded_commitment_tx) locator = decoded_commitment_tx.get("txid")[:LOCATOR_LEN_HEX] tracker_data = dict( locator=locator, dispute_txid=bitcoin_cli.decoderawtransaction(commitment_tx).get( "txid"), penalty_txid=bitcoin_cli.decoderawtransaction(penalty_tx).get( "txid"), penalty_rawtx=penalty_tx, user_id="02" + get_random_value_hex(32), ) return TransactionTracker.from_dict(tracker_data)
def create_dummy_tracker(random_txid=False, penalty_rawtx=None): locator, dispute_txid, penalty_txid, penalty_rawtx, appointment_end = create_dummy_tracker_data( random_txid, penalty_rawtx) return TransactionTracker(locator, dispute_txid, penalty_txid, penalty_rawtx, appointment_end)
def test_tracker_from_dict(): tracker_dict = create_dummy_tracker().to_dict() new_tracker = TransactionTracker.from_dict(tracker_dict) assert tracker_dict == new_tracker.to_dict()
def test_tracker_from_dict(generate_dummy_tracker): # Check that a tracker can be created from a dictionary tracker_dict = generate_dummy_tracker().to_dict() new_tracker = TransactionTracker.from_dict(tracker_dict) assert tracker_dict == new_tracker.to_dict()