def test_send_transaction(carrier): tx = create_commitment_tx() txid = bitcoin_cli.decoderawtransaction(tx).get("txid") receipt = carrier.send_transaction(tx, txid) assert receipt.delivered is True
def test_send_transaction_bitcoind_crash(carrier): # Trying to send a transaction if bitcoind is unreachable should block the thread until it becomes reachable again tx = create_commitment_tx() txid = bitcoin_cli.decoderawtransaction(tx).get("txid") run_test_blocking_command_bitcoind_crash( carrier.bitcoind_reachable, lambda: carrier.send_transaction(tx, txid), )
def test_get_completed_trackers(db_manager, gatekeeper, carrier, responder, block_processor, generate_dummy_tracker): commitment_txs = [create_commitment_tx() for _ in range(30)] generate_block_with_transactions(commitment_txs) # A complete tracker is a tracker whose penalty transaction has been irrevocably resolved (i.e. has reached 100 # confirmations) # We'll create 3 type of txs: irrevocably resolved, confirmed but not irrevocably resolved, and unconfirmed trackers_ir_resolved = { uuid4().hex: generate_dummy_tracker(commitment_tx) for commitment_tx in commitment_txs[:10] } trackers_confirmed = { uuid4().hex: generate_dummy_tracker(commitment_tx) for commitment_tx in commitment_txs[10:20] } trackers_unconfirmed = {} for commitment_tx in commitment_txs[20:]: tracker = generate_dummy_tracker(commitment_tx) responder.unconfirmed_txs.append(tracker.penalty_txid) trackers_unconfirmed[uuid4().hex] = tracker all_trackers = {} all_trackers.update(trackers_ir_resolved) all_trackers.update(trackers_confirmed) all_trackers.update(trackers_unconfirmed) # Let's add all to the Responder for uuid, tracker in all_trackers.items(): responder.trackers[uuid] = tracker.get_summary() for uuid, tracker in trackers_ir_resolved.items(): bitcoin_cli.sendrawtransaction(tracker.penalty_rawtx) generate_blocks_with_delay(1) for uuid, tracker in trackers_confirmed.items(): bitcoin_cli.sendrawtransaction(tracker.penalty_rawtx) # ir_resolved have 100 confirmations and confirmed have 99 generate_blocks_with_delay(99) # Let's check completed_trackers = responder.get_completed_trackers() ended_trackers_keys = list(trackers_ir_resolved.keys()) assert set(completed_trackers) == set(ended_trackers_keys) # Generating 1 additional blocks should also include confirmed generate_blocks_with_delay(1) completed_trackers = responder.get_completed_trackers() ended_trackers_keys.extend(list(trackers_confirmed.keys())) assert set(completed_trackers) == set(ended_trackers_keys)
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 test_send_double_spending_transaction(carrier): # We can test what happens if the same transaction is sent twice tx = create_commitment_tx() txid = bitcoin_cli.decoderawtransaction(tx).get("txid") receipt = carrier.send_transaction(tx, txid) sent_txs.append(txid) # Wait for a block to be mined. Issued receipts are reset from the Responder every block, so we should do it too. generate_blocks(2) carrier.issued_receipts = {} # Try to send it again receipt2 = carrier.send_transaction(tx, txid) # The carrier should report delivered True for both, but in the second case the transaction was already delivered # (either by himself or someone else) assert receipt.delivered is True assert receipt2.delivered is True and receipt2.confirmations >= 1 and receipt2.reason == RPC_VERIFY_ALREADY_IN_CHAIN
def test_handle_breach(db_manager, gatekeeper, carrier, responder, block_processor, generate_dummy_tracker): uuid = uuid4().hex commitment_tx = create_commitment_tx() tracker = generate_dummy_tracker(commitment_tx) generate_block_with_transactions(commitment_tx) # The block_hash passed to add_response does not matter much now. It will in the future to deal with errors receipt = responder.handle_breach( tracker.locator, uuid, tracker.dispute_txid, tracker.penalty_txid, tracker.penalty_rawtx, tracker.user_id, block_hash=get_random_value_hex(32), ) assert receipt.delivered is True
def test_rebroadcast(db_manager, gatekeeper, carrier, responder, block_processor, generate_dummy_tracker): # Include the commitment txs in a block commitment_txs = [create_commitment_tx() for _ in range(20)] generate_block_with_transactions(commitment_txs) txs_to_rebroadcast = [] # Rebroadcast calls add_response with retry=True. The tracker data is already in trackers. for i, commitment_tx in enumerate(commitment_txs): uuid = uuid4().hex tracker = generate_dummy_tracker(commitment_tx) responder.trackers[uuid] = { "locator": tracker.locator, "penalty_txid": tracker.penalty_txid, "user_id": tracker.user_id, } # 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_dict()) responder.tx_tracker_map[tracker.penalty_txid] = [uuid] responder.unconfirmed_txs.append(tracker.penalty_txid) # Let's add some of the txs in the rebroadcast list if (i % 2) == 0: txs_to_rebroadcast.append(tracker.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_handle_breach_bad_response(db_manager, gatekeeper, carrier, responder, block_processor, generate_dummy_tracker): # We need a new carrier here, otherwise the transaction will be flagged as previously sent and receipt.delivered # will be True uuid = uuid4().hex commitment_tx = create_commitment_tx() tracker = generate_dummy_tracker(commitment_tx) # The block_hash passed to add_response does not matter much now. It will in the future to deal with errors receipt = responder.handle_breach( tracker.locator, uuid, tracker.dispute_txid, tracker.penalty_txid, tracker.penalty_rawtx, tracker.user_id, block_hash=get_random_value_hex(32), ) # Notice we are not minting a block with the commitment transaction, so broadcasting the penalty will fail assert receipt.delivered is False
def test_send_transaction_invalid_format(carrier): # Test sending a transaction that does not fits the format txid = create_commitment_tx()[::-1] receipt = carrier.send_transaction(txid, txid) assert receipt.delivered is False and receipt.reason == RPC_DESERIALIZATION_ERROR
def test_do_watch(temp_db_manager, gatekeeper, carrier, block_processor, generate_dummy_tracker): commitment_txs = [create_commitment_tx() for _ in range(20)] trackers = [ generate_dummy_tracker(commitment_tx) for commitment_tx in commitment_txs ] subscription_expiry = block_processor.get_block_count() + 110 # Broadcast all commitment transactions generate_block_with_transactions(commitment_txs) # Create a fresh responder to simplify the test responder = Responder(temp_db_manager, gatekeeper, carrier, block_processor) chain_monitor = ChainMonitor([Queue(), responder.block_queue], block_processor, bitcoind_feed_params) chain_monitor.monitor_chain() chain_monitor.activate() # Let's set up the trackers first for tracker in trackers: uuid = uuid4().hex # Simulate user registration so trackers can properly expire responder.gatekeeper.registered_users[tracker.user_id] = UserInfo( available_slots=10, subscription_expiry=subscription_expiry) # Add data to the Responder responder.trackers[uuid] = tracker.get_summary() responder.tx_tracker_map[tracker.penalty_txid] = [uuid] responder.missed_confirmations[tracker.penalty_txid] = 0 responder.unconfirmed_txs.append(tracker.penalty_txid) # Assuming the appointment only took a single slot responder.gatekeeper.registered_users[ tracker.user_id].appointments[uuid] = 1 # We also need to store the info in the db responder.db_manager.create_triggered_appointment_flag(uuid) responder.db_manager.store_responder_tracker(uuid, tracker.to_dict()) # Let's start to watch do_watch_thread = Thread(target=responder.do_watch, daemon=True) do_watch_thread.start() # And broadcast some of the penalties broadcast_txs = [] for tracker in trackers[:5]: bitcoin_cli.sendrawtransaction(tracker.penalty_rawtx) broadcast_txs.append(tracker.penalty_txid) # Mine a block generate_blocks_with_delay(1) # The transactions we sent shouldn't be in the unconfirmed transaction list anymore assert not set(broadcast_txs).issubset(responder.unconfirmed_txs) # CONFIRMATIONS_BEFORE_RETRY+1 blocks after, the responder should rebroadcast the unconfirmed txs (15 remaining) generate_blocks_with_delay(CONFIRMATIONS_BEFORE_RETRY + 1) assert len(responder.unconfirmed_txs) == 0 assert len(responder.trackers) == 20 # Generating 100 - CONFIRMATIONS_BEFORE_RETRY -2 additional blocks should complete the first 5 trackers generate_blocks_with_delay(100 - CONFIRMATIONS_BEFORE_RETRY - 2) assert len(responder.unconfirmed_txs) == 0 assert len(responder.trackers) == 15 # Check they are not in the Gatekeeper either for tracker in trackers[:5]: assert len(responder.gatekeeper.registered_users[ tracker.user_id].appointments) == 0 # CONFIRMATIONS_BEFORE_RETRY additional blocks should complete the rest generate_blocks_with_delay(CONFIRMATIONS_BEFORE_RETRY) assert len(responder.unconfirmed_txs) == 0 assert len(responder.trackers) == 0 # Check they are not in the Gatekeeper either for tracker in trackers[5:]: assert len(responder.gatekeeper.registered_users[ tracker.user_id].appointments) == 0 chain_monitor.terminate() do_watch_thread.join()