Пример #1
0
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
Пример #2
0
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),
    )
Пример #3
0
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)
Пример #4
0
    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)
Пример #5
0
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
Пример #6
0
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
Пример #7
0
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
Пример #8
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
Пример #9
0
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
Пример #10
0
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()