示例#1
0
def test_sendrawtransaction(genesis_block_hash):
    # sendrawtransaction should only allow txs that the simulator has not mined yet
    bitcoin_cli().sendrawtransaction(
        transaction.create_dummy_transaction().hex())

    # Any tx with invalid format or that matches with an already mined transaction should fail
    try:
        # Trying to resend the coinbase tx of the genesis block
        genesis_tx = bitcoin_cli().getblock(genesis_block_hash).get("tx")[0]
        bitcoin_cli().sendrawtransaction(genesis_tx)
        assert False

    except JSONRPCException as e:
        assert True

    for v in MIXED_VALUES:
        # Sending random values
        try:
            bitcoin_cli().sendrawtransaction(v)
            assert False
        except JSONRPCException as e:
            assert True

    # Trying with a valid tx
    try:
        tx = transaction.create_dummy_transaction()
        bitcoin_cli().sendrawtransaction(tx.hex())
        assert True

    except JSONRPCException as e:
        assert False
示例#2
0
def test_decoderawtransaction(genesis_block_hash):
    # decoderawtransaction should only return if the given transaction is properly formatted (can be deserialized using
    # (TX.deserialize(raw_tx).
    block = bitcoin_cli().getblock(genesis_block_hash)
    coinbase_txid = block.get("tx")[0]

    coinbase_tx = bitcoin_cli().getrawtransaction(coinbase_txid).get("hex")
    tx = bitcoin_cli().decoderawtransaction(coinbase_tx)

    assert isinstance(tx, dict)
    assert isinstance(tx.get("txid"), str)
    assert check_hash_format(tx.get("txid"))

    # Therefore it should also work for a random transaction hex in our simulation
    random_tx = transaction.create_dummy_transaction()
    tx = bitcoin_cli().decoderawtransaction(random_tx.hex())
    assert isinstance(tx, dict)
    assert isinstance(tx.get("txid"), str)
    assert check_hash_format(tx.get("txid"))

    # But it should fail for not proper formatted one
    for v in MIXED_VALUES:
        try:
            bitcoin_cli().decoderawtransaction(v)
            assert False
        except JSONRPCException as e:
            assert True
示例#3
0
def test_add_tracker_already_confirmed(responder):
    for i in range(20):
        uuid = uuid4().hex
        confirmations = i + 1
        locator, dispute_txid, penalty_txid, penalty_rawtx, appointment_end = create_dummy_tracker_data(
            penalty_rawtx=create_dummy_transaction().hex())

        responder.add_tracker(uuid, locator, dispute_txid, penalty_txid,
                              penalty_rawtx, appointment_end, confirmations)

        assert penalty_txid not in responder.unconfirmed_txs
示例#4
0
    def get_new_block_data():
        """
        Creates the data to be used for a mined block.

       Returns:
            :obj:`tuple`: A three item tuple (block_hash, coinbase_tx, coinbase_tx_hash)
        """
        block_hash = utils.get_random_value_hex(32)
        coinbase_tx = transaction.create_dummy_transaction()

        return block_hash, coinbase_tx.hex(), coinbase_tx.tx_id.hex()
示例#5
0
def generate_dummy_appointment_data(real_height=True, start_time_offset=5, end_time_offset=30):
    if real_height:
        current_height = bitcoin_cli(bitcoind_connect_params).getblockcount()

    else:
        current_height = 10

    dispute_tx = create_dummy_transaction()
    dispute_txid = dispute_tx.tx_id.hex()
    penalty_tx = create_dummy_transaction(dispute_txid)

    dummy_appointment_data = {
        "tx": penalty_tx.hex(),
        "tx_id": dispute_txid,
        "start_time": current_height + start_time_offset,
        "end_time": current_height + end_time_offset,
        "to_self_delay": 20,
    }

    # dummy keys for this test
    client_sk, client_pk = generate_keypair()
    client_pk_hex = client_pk.format().hex()

    locator = compute_locator(dispute_txid)
    blob = Blob(dummy_appointment_data.get("tx"))

    encrypted_blob = Cryptographer.encrypt(blob, dummy_appointment_data.get("tx_id"))

    appointment_data = {
        "locator": locator,
        "start_time": dummy_appointment_data.get("start_time"),
        "end_time": dummy_appointment_data.get("end_time"),
        "to_self_delay": dummy_appointment_data.get("to_self_delay"),
        "encrypted_blob": encrypted_blob,
    }

    signature = Cryptographer.sign(Appointment.from_dict(appointment_data).serialize(), client_sk)

    data = {"appointment": appointment_data, "signature": signature, "public_key": client_pk_hex}

    return data, dispute_tx.hex()
示例#6
0
def generate_dummy_appointment():
    dispute_tx = create_dummy_transaction()
    dispute_txid = dispute_tx.tx_id.hex()
    penalty_tx = create_dummy_transaction(dispute_txid)

    locator = compute_locator(dispute_txid)
    dummy_appointment_data = {
        "tx": penalty_tx.hex(),
        "tx_id": dispute_txid,
        "to_self_delay": 20
    }
    encrypted_blob = Cryptographer.encrypt(dummy_appointment_data.get("tx"),
                                           dummy_appointment_data.get("tx_id"))

    appointment_data = {
        "locator": locator,
        "to_self_delay": dummy_appointment_data.get("to_self_delay"),
        "encrypted_blob": encrypted_blob,
        "user_id": get_random_value_hex(16),
    }

    return ExtendedAppointment.from_dict(appointment_data), dispute_tx.hex()
def test_add_tracker_already_confirmed(responder):
    for i in range(20):
        uuid = uuid4().hex
        confirmations = i + 1
        locator, dispute_txid, penalty_txid, penalty_rawtx, user_id = create_dummy_tracker_data(
            penalty_rawtx=create_dummy_transaction().hex())

        responder.add_tracker(uuid, locator, dispute_txid, penalty_txid,
                              penalty_rawtx, user_id, confirmations)

        assert penalty_txid not in responder.unconfirmed_txs
        assert (responder.trackers[uuid].get("penalty_txid") == penalty_txid
                and responder.trackers[uuid].get("locator") == locator
                and responder.trackers[uuid].get("user_id") == user_id)
示例#8
0
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
示例#9
0
def test_send_double_spending_transaction(carrier):
    # We can test what happens if the same transaction is sent twice
    tx = create_dummy_transaction()
    txid = tx.tx_id.hex()

    receipt = carrier.send_transaction(tx.hex(), txid)
    sent_txs.append(txid)

    # Wait for a block to be mined. Issued receipts is 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.hex(), 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
示例#10
0
def test_send_transaction_invalid_format(carrier):
    # Test sending a transaction that does not fits the format
    txid = create_dummy_transaction().tx_id.hex()
    receipt = carrier.send_transaction(txid, txid)

    assert receipt.delivered is False and receipt.reason == RPC_DESERIALIZATION_ERROR
示例#11
0
def test_send_transaction(run_bitcoind, carrier):
    tx = create_dummy_transaction()

    receipt = carrier.send_transaction(tx.hex(), tx.tx_id.hex())

    assert receipt.delivered is True
示例#12
0
def test_get_completed_trackers(db_manager, carrier, block_processor):
    initial_height = bitcoin_cli(bitcoind_connect_params).getblockcount()

    responder = Responder(db_manager, carrier, block_processor)
    chain_monitor = ChainMonitor(Queue(), responder.block_queue,
                                 block_processor, bitcoind_feed_params)
    chain_monitor.monitor_chain()

    # A complete tracker is a tracker that has reached the appointment end with enough confs (> MIN_CONFIRMATIONS)
    # We'll create three type of transactions: end reached + enough conf, end reached + no enough conf, end not reached
    trackers_end_conf = {
        uuid4().hex:
        create_dummy_tracker(penalty_rawtx=create_dummy_transaction().hex())
        for _ in range(10)
    }

    trackers_end_no_conf = {}
    for _ in range(10):
        tracker = create_dummy_tracker(
            penalty_rawtx=create_dummy_transaction().hex())
        responder.unconfirmed_txs.append(tracker.penalty_txid)
        trackers_end_no_conf[uuid4().hex] = tracker

    trackers_no_end = {}
    for _ in range(10):
        tracker = create_dummy_tracker(
            penalty_rawtx=create_dummy_transaction().hex())
        tracker.appointment_end += 10
        trackers_no_end[uuid4().hex] = tracker

    all_trackers = {}
    all_trackers.update(trackers_end_conf)
    all_trackers.update(trackers_end_no_conf)
    all_trackers.update(trackers_no_end)

    # Let's add all to the  responder
    for uuid, tracker in all_trackers.items():
        responder.trackers[uuid] = {
            "locator": tracker.locator,
            "penalty_txid": tracker.penalty_txid,
            "appointment_end": tracker.appointment_end,
        }

    for uuid, tracker in all_trackers.items():
        bitcoin_cli(bitcoind_connect_params).sendrawtransaction(
            tracker.penalty_rawtx)

    # The dummy appointments have a end_appointment time of current + 2, but trackers need at least 6 confs by default
    generate_blocks(6)

    # And now let's check
    completed_trackers = responder.get_completed_trackers(initial_height + 6)
    completed_trackers_ids = [
        tracker_id for tracker_id, confirmations in completed_trackers.items()
    ]
    ended_trackers_keys = list(trackers_end_conf.keys())
    assert set(completed_trackers_ids) == set(ended_trackers_keys)

    # Generating 6 additional blocks should also confirm trackers_no_end
    generate_blocks(6)

    completed_trackers = responder.get_completed_trackers(initial_height + 12)
    completed_trackers_ids = [
        tracker_id for tracker_id, confirmations in completed_trackers.items()
    ]
    ended_trackers_keys.extend(list(trackers_no_end.keys()))

    assert set(completed_trackers_ids) == set(ended_trackers_keys)
示例#13
0
def test_do_watch(temp_db_manager, carrier, block_processor):
    # Create a fresh responder to simplify the test
    responder = Responder(temp_db_manager, carrier, block_processor)
    chain_monitor = ChainMonitor(Queue(), responder.block_queue,
                                 block_processor, bitcoind_feed_params)
    chain_monitor.monitor_chain()

    trackers = [
        create_dummy_tracker(penalty_rawtx=create_dummy_transaction().hex())
        for _ in range(20)
    ]

    # Let's set up the trackers first
    for tracker in trackers:
        uuid = uuid4().hex

        responder.trackers[uuid] = {
            "locator": tracker.locator,
            "penalty_txid": tracker.penalty_txid,
            "appointment_end": tracker.appointment_end,
        }
        responder.tx_tracker_map[tracker.penalty_txid] = [uuid]
        responder.missed_confirmations[tracker.penalty_txid] = 0
        responder.unconfirmed_txs.append(tracker.penalty_txid)

        # 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_json())

    # Let's start to watch
    Thread(target=responder.do_watch, daemon=True).start()

    # And broadcast some of the transactions
    broadcast_txs = []
    for tracker in trackers[:5]:
        bitcoin_cli(bitcoind_connect_params).sendrawtransaction(
            tracker.penalty_rawtx)
        broadcast_txs.append(tracker.penalty_txid)

    # Mine a block
    generate_block()

    # The transactions we sent shouldn't be in the unconfirmed transaction list anymore
    assert not set(broadcast_txs).issubset(responder.unconfirmed_txs)

    # TODO: test that reorgs can be detected once data persistence is merged (new version of the simulator)

    # Generating 5 additional blocks should complete the 5 trackers
    generate_blocks(5)

    assert not set(broadcast_txs).issubset(responder.tx_tracker_map)

    # Do the rest
    broadcast_txs = []
    for tracker in trackers[5:]:
        bitcoin_cli(bitcoind_connect_params).sendrawtransaction(
            tracker.penalty_rawtx)
        broadcast_txs.append(tracker.penalty_txid)

    # Mine a block
    generate_blocks(6)

    assert len(responder.tx_tracker_map) == 0
def test_get_expired_trackers(responder):
    # expired trackers are those who's subscription has reached the expiry block and have not been confirmed.
    # confirmed trackers that have reached their expiry will be kept until completed
    current_block = responder.block_processor.get_block_count()

    # Lets first register the a couple of users
    user1_id = get_random_value_hex(16)
    responder.gatekeeper.registered_users[user1_id] = UserInfo(
        available_slots=10, subscription_expiry=current_block + 15)
    user2_id = get_random_value_hex(16)
    responder.gatekeeper.registered_users[user2_id] = UserInfo(
        available_slots=10, subscription_expiry=current_block + 16)

    # And create some trackers and add them to the corresponding user in the Gatekeeper
    expired_unconfirmed_trackers_15 = {}
    expired_unconfirmed_trackers_16 = {}
    expired_confirmed_trackers_15 = {}
    for _ in range(10):
        uuid = uuid4().hex
        dummy_tracker = create_dummy_tracker(
            penalty_rawtx=create_dummy_transaction().hex())
        dummy_tracker.user_id = user1_id
        expired_unconfirmed_trackers_15[uuid] = dummy_tracker
        responder.unconfirmed_txs.append(dummy_tracker.penalty_txid)
        # Assume the appointment only took a single slot
        responder.gatekeeper.registered_users[
            dummy_tracker.user_id].appointments[uuid] = 1

    for _ in range(10):
        uuid = uuid4().hex
        dummy_tracker = create_dummy_tracker(
            penalty_rawtx=create_dummy_transaction().hex())
        dummy_tracker.user_id = user1_id
        expired_confirmed_trackers_15[uuid] = dummy_tracker
        # Assume the appointment only took a single slot
        responder.gatekeeper.registered_users[
            dummy_tracker.user_id].appointments[uuid] = 1

    for _ in range(10):
        uuid = uuid4().hex
        dummy_tracker = create_dummy_tracker(
            penalty_rawtx=create_dummy_transaction().hex())
        dummy_tracker.user_id = user2_id
        expired_unconfirmed_trackers_16[uuid] = dummy_tracker
        responder.unconfirmed_txs.append(dummy_tracker.penalty_txid)
        # Assume the appointment only took a single slot
        responder.gatekeeper.registered_users[
            dummy_tracker.user_id].appointments[uuid] = 1

    all_trackers = {}
    all_trackers.update(expired_confirmed_trackers_15)
    all_trackers.update(expired_unconfirmed_trackers_15)
    all_trackers.update(expired_unconfirmed_trackers_16)

    # Add everything to the Responder
    for uuid, tracker in all_trackers.items():
        responder.trackers[uuid] = tracker.get_summary()

    # Currently nothing should be expired
    assert responder.get_expired_trackers(current_block) == []

    # 15 blocks (+ EXPIRY_DELTA) afterwards only user1's confirmed trackers should be expired
    assert responder.get_expired_trackers(
        current_block + 15 + config.get("EXPIRY_DELTA")) == list(
            expired_unconfirmed_trackers_15.keys())

    # 1 (+ EXPIRY_DELTA) block after that user2's should be expired
    assert responder.get_expired_trackers(
        current_block + 16 + config.get("EXPIRY_DELTA")) == list(
            expired_unconfirmed_trackers_16.keys())
def test_get_completed_trackers(db_manager, gatekeeper, carrier,
                                block_processor):
    responder = Responder(db_manager, gatekeeper, carrier, block_processor)
    chain_monitor = ChainMonitor(Queue(), responder.block_queue,
                                 block_processor, bitcoind_feed_params)
    chain_monitor.monitor_chain()

    # A complete tracker is a tracker which 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:
        create_dummy_tracker(penalty_rawtx=create_dummy_transaction().hex())
        for _ in range(10)
    }

    trackers_confirmed = {
        uuid4().hex:
        create_dummy_tracker(penalty_rawtx=create_dummy_transaction().hex())
        for _ in range(10)
    }

    trackers_unconfirmed = {}
    for _ in range(10):
        tracker = create_dummy_tracker(
            penalty_rawtx=create_dummy_transaction().hex())
        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(bitcoind_connect_params).sendrawtransaction(
            tracker.penalty_rawtx)

    generate_block_w_delay()

    for uuid, tracker in trackers_confirmed.items():
        bitcoin_cli(bitcoind_connect_params).sendrawtransaction(
            tracker.penalty_rawtx)

    # ir_resolved have 100 confirmations and confirmed have 99
    generate_blocks_w_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_block_w_delay()

    completed_trackers = responder.get_completed_trackers()
    ended_trackers_keys.extend(list(trackers_confirmed.keys()))
    assert set(completed_trackers) == set(ended_trackers_keys)
def test_do_watch(temp_db_manager, gatekeeper, carrier, block_processor):
    # 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()

    trackers = [
        create_dummy_tracker(penalty_rawtx=create_dummy_transaction().hex())
        for _ in range(20)
    ]
    subscription_expiry = responder.block_processor.get_block_count() + 110

    # 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
    Thread(target=responder.do_watch, daemon=True).start()

    # And broadcast some of the transactions
    broadcast_txs = []
    for tracker in trackers[:5]:
        bitcoin_cli(bitcoind_connect_params).sendrawtransaction(
            tracker.penalty_rawtx)
        broadcast_txs.append(tracker.penalty_txid)

    # Mine a block
    generate_block_w_delay()

    # 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_w_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_w_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_w_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