import pytest
from teos.watcher import InvalidTransactionFormat
from test.teos.conftest import generate_blocks
from test.teos.unit.conftest import get_random_value_hex, fork


hex_tx = (
    "0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402"
    "204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4"
    "acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b"
    "13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1ba"
    "ded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482e"
    "cad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000"
)


def test_get_best_block_hash(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    # As long as bitcoind is running we should always get a block hash
    assert best_block_hash is not None and isinstance(best_block_hash, str)


def test_get_block(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    # Getting a block from a block hash we are aware of should return data
    block = block_processor.get_block(best_block_hash)

    # Checking that the received block has at least the fields we need
    assert isinstance(block, dict)
    assert block.get("hash") == best_block_hash and "height" in block and "previousblockhash" in block and "tx" in block


def test_get_random_block(block_processor):
    block = block_processor.get_block(get_random_value_hex(32))

    assert block is None


def test_get_block_count(block_processor):
    block_count = block_processor.get_block_count()
    assert isinstance(block_count, int) and block_count >= 0


def test_decode_raw_transaction(block_processor):
    # We cannot exhaustively test this (we rely on bitcoind for this) but we can try to decode a correct transaction
    assert block_processor.decode_raw_transaction(hex_tx) is not None


def test_decode_raw_transaction_invalid(block_processor):
    # Same but with an invalid one
    with pytest.raises(InvalidTransactionFormat):
        block_processor.decode_raw_transaction(hex_tx[::-1])


def test_get_missed_blocks(block_processor):
    target_block = block_processor.get_best_block_hash()

    # Generate some blocks and store the hash in a list
    missed_blocks = generate_blocks(5)

    # Check what we've missed
    assert block_processor.get_missed_blocks(target_block) == missed_blocks

    # We can see how it does not work if we replace the target by the first element in the list
    block_tip = missed_blocks[0]
    assert block_processor.get_missed_blocks(block_tip) != missed_blocks

    # But it does again if we skip that block
    assert block_processor.get_missed_blocks(block_tip) == missed_blocks[1:]


def test_get_distance_to_tip(block_processor):
    target_distance = 5

    target_block = block_processor.get_best_block_hash()

    # Mine some blocks up to the target distance
    generate_blocks(target_distance)

    # Check if the distance is properly computed
    assert block_processor.get_distance_to_tip(target_block) == target_distance


def test_is_block_in_best_chain(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    best_block = block_processor.get_block(best_block_hash)

    assert block_processor.is_block_in_best_chain(best_block_hash)

    fork(best_block.get("previousblockhash"), 2)

    assert not block_processor.is_block_in_best_chain(best_block_hash)


def test_find_last_common_ancestor(block_processor):
    ancestor = block_processor.get_best_block_hash()
    blocks = generate_blocks(3)
    best_block_hash = blocks[-1]

    # Create a fork (invalidate the next block after the ancestor and mine 4 blocks on top)
    fork(blocks[0], 4)

    # The last common ancestor between the old best and the new best should be the "ancestor"
    last_common_ancestor, dropped_txs = block_processor.find_last_common_ancestor(best_block_hash)
    assert last_common_ancestor == ancestor
    assert len(dropped_txs) == 3

09d15385359fa38a576729b68ecb250cbfc72363
<mask token>


def test_get_best_block_hash(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    assert best_block_hash is not None and isinstance(best_block_hash, str)


def test_get_block(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    block = block_processor.get_block(best_block_hash)
    assert isinstance(block, dict)
    assert block.get('hash'
        ) == best_block_hash and 'height' in block and 'previousblockhash' in block and 'tx' in block


def test_get_random_block(block_processor):
    block = block_processor.get_block(get_random_value_hex(32))
    assert block is None


def test_get_block_count(block_processor):
    block_count = block_processor.get_block_count()
    assert isinstance(block_count, int) and block_count >= 0


def test_decode_raw_transaction(block_processor):
    assert block_processor.decode_raw_transaction(hex_tx) is not None


<mask token>


def test_get_missed_blocks(block_processor):
    target_block = block_processor.get_best_block_hash()
    missed_blocks = generate_blocks(5)
    assert block_processor.get_missed_blocks(target_block) == missed_blocks
    block_tip = missed_blocks[0]
    assert block_processor.get_missed_blocks(block_tip) != missed_blocks
    assert block_processor.get_missed_blocks(block_tip) == missed_blocks[1:]


<mask token>


def test_is_block_in_best_chain(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    best_block = block_processor.get_block(best_block_hash)
    assert block_processor.is_block_in_best_chain(best_block_hash)
    fork(best_block.get('previousblockhash'), 2)
    assert not block_processor.is_block_in_best_chain(best_block_hash)


def test_find_last_common_ancestor(block_processor):
    ancestor = block_processor.get_best_block_hash()
    blocks = generate_blocks(3)
    best_block_hash = blocks[-1]
    fork(blocks[0], 4)
    last_common_ancestor, dropped_txs = (block_processor.
        find_last_common_ancestor(best_block_hash))
    assert last_common_ancestor == ancestor
    assert len(dropped_txs) == 3

<mask token>


def test_get_best_block_hash(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    assert best_block_hash is not None and isinstance(best_block_hash, str)


def test_get_block(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    block = block_processor.get_block(best_block_hash)
    assert isinstance(block, dict)
    assert block.get('hash'
        ) == best_block_hash and 'height' in block and 'previousblockhash' in block and 'tx' in block


def test_get_random_block(block_processor):
    block = block_processor.get_block(get_random_value_hex(32))
    assert block is None


def test_get_block_count(block_processor):
    block_count = block_processor.get_block_count()
    assert isinstance(block_count, int) and block_count >= 0


def test_decode_raw_transaction(block_processor):
    assert block_processor.decode_raw_transaction(hex_tx) is not None


<mask token>


def test_get_missed_blocks(block_processor):
    target_block = block_processor.get_best_block_hash()
    missed_blocks = generate_blocks(5)
    assert block_processor.get_missed_blocks(target_block) == missed_blocks
    block_tip = missed_blocks[0]
    assert block_processor.get_missed_blocks(block_tip) != missed_blocks
    assert block_processor.get_missed_blocks(block_tip) == missed_blocks[1:]


def test_get_distance_to_tip(block_processor):
    target_distance = 5
    target_block = block_processor.get_best_block_hash()
    generate_blocks(target_distance)
    assert block_processor.get_distance_to_tip(target_block) == target_distance


def test_is_block_in_best_chain(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    best_block = block_processor.get_block(best_block_hash)
    assert block_processor.is_block_in_best_chain(best_block_hash)
    fork(best_block.get('previousblockhash'), 2)
    assert not block_processor.is_block_in_best_chain(best_block_hash)


def test_find_last_common_ancestor(block_processor):
    ancestor = block_processor.get_best_block_hash()
    blocks = generate_blocks(3)
    best_block_hash = blocks[-1]
    fork(blocks[0], 4)
    last_common_ancestor, dropped_txs = (block_processor.
        find_last_common_ancestor(best_block_hash))
    assert last_common_ancestor == ancestor
    assert len(dropped_txs) == 3

<mask token>
hex_tx = (
    '0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000'
    )


def test_get_best_block_hash(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    assert best_block_hash is not None and isinstance(best_block_hash, str)


def test_get_block(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    block = block_processor.get_block(best_block_hash)
    assert isinstance(block, dict)
    assert block.get('hash'
        ) == best_block_hash and 'height' in block and 'previousblockhash' in block and 'tx' in block


def test_get_random_block(block_processor):
    block = block_processor.get_block(get_random_value_hex(32))
    assert block is None


def test_get_block_count(block_processor):
    block_count = block_processor.get_block_count()
    assert isinstance(block_count, int) and block_count >= 0


def test_decode_raw_transaction(block_processor):
    assert block_processor.decode_raw_transaction(hex_tx) is not None


def test_decode_raw_transaction_invalid(block_processor):
    with pytest.raises(InvalidTransactionFormat):
        block_processor.decode_raw_transaction(hex_tx[::-1])


def test_get_missed_blocks(block_processor):
    target_block = block_processor.get_best_block_hash()
    missed_blocks = generate_blocks(5)
    assert block_processor.get_missed_blocks(target_block) == missed_blocks
    block_tip = missed_blocks[0]
    assert block_processor.get_missed_blocks(block_tip) != missed_blocks
    assert block_processor.get_missed_blocks(block_tip) == missed_blocks[1:]


def test_get_distance_to_tip(block_processor):
    target_distance = 5
    target_block = block_processor.get_best_block_hash()
    generate_blocks(target_distance)
    assert block_processor.get_distance_to_tip(target_block) == target_distance


def test_is_block_in_best_chain(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    best_block = block_processor.get_block(best_block_hash)
    assert block_processor.is_block_in_best_chain(best_block_hash)
    fork(best_block.get('previousblockhash'), 2)
    assert not block_processor.is_block_in_best_chain(best_block_hash)


def test_find_last_common_ancestor(block_processor):
    ancestor = block_processor.get_best_block_hash()
    blocks = generate_blocks(3)
    best_block_hash = blocks[-1]
    fork(blocks[0], 4)
    last_common_ancestor, dropped_txs = (block_processor.
        find_last_common_ancestor(best_block_hash))
    assert last_common_ancestor == ancestor
    assert len(dropped_txs) == 3

import pytest
from teos.watcher import InvalidTransactionFormat
from test.teos.conftest import generate_blocks
from test.teos.unit.conftest import get_random_value_hex, fork
hex_tx = (
    '0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000'
    )


def test_get_best_block_hash(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    assert best_block_hash is not None and isinstance(best_block_hash, str)


def test_get_block(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    block = block_processor.get_block(best_block_hash)
    assert isinstance(block, dict)
    assert block.get('hash'
        ) == best_block_hash and 'height' in block and 'previousblockhash' in block and 'tx' in block


def test_get_random_block(block_processor):
    block = block_processor.get_block(get_random_value_hex(32))
    assert block is None


def test_get_block_count(block_processor):
    block_count = block_processor.get_block_count()
    assert isinstance(block_count, int) and block_count >= 0


def test_decode_raw_transaction(block_processor):
    assert block_processor.decode_raw_transaction(hex_tx) is not None


def test_decode_raw_transaction_invalid(block_processor):
    with pytest.raises(InvalidTransactionFormat):
        block_processor.decode_raw_transaction(hex_tx[::-1])


def test_get_missed_blocks(block_processor):
    target_block = block_processor.get_best_block_hash()
    missed_blocks = generate_blocks(5)
    assert block_processor.get_missed_blocks(target_block) == missed_blocks
    block_tip = missed_blocks[0]
    assert block_processor.get_missed_blocks(block_tip) != missed_blocks
    assert block_processor.get_missed_blocks(block_tip) == missed_blocks[1:]


def test_get_distance_to_tip(block_processor):
    target_distance = 5
    target_block = block_processor.get_best_block_hash()
    generate_blocks(target_distance)
    assert block_processor.get_distance_to_tip(target_block) == target_distance


def test_is_block_in_best_chain(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    best_block = block_processor.get_block(best_block_hash)
    assert block_processor.is_block_in_best_chain(best_block_hash)
    fork(best_block.get('previousblockhash'), 2)
    assert not block_processor.is_block_in_best_chain(best_block_hash)


def test_find_last_common_ancestor(block_processor):
    ancestor = block_processor.get_best_block_hash()
    blocks = generate_blocks(3)
    best_block_hash = blocks[-1]
    fork(blocks[0], 4)
    last_common_ancestor, dropped_txs = (block_processor.
        find_last_common_ancestor(best_block_hash))
    assert last_common_ancestor == ancestor
    assert len(dropped_txs) == 3

import pytest
from teos.watcher import InvalidTransactionFormat
from test.teos.conftest import generate_blocks
from test.teos.unit.conftest import get_random_value_hex, fork


hex_tx = (
    "0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402"
    "204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4"
    "acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b"
    "13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1ba"
    "ded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482e"
    "cad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000"
)


def test_get_best_block_hash(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    # As long as bitcoind is running we should always get a block hash
    assert best_block_hash is not None and isinstance(best_block_hash, str)


def test_get_block(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    # Getting a block from a block hash we are aware of should return data
    block = block_processor.get_block(best_block_hash)

    # Checking that the received block has at least the fields we need
    assert isinstance(block, dict)
    assert block.get("hash") == best_block_hash and "height" in block and "previousblockhash" in block and "tx" in block


def test_get_random_block(block_processor):
    block = block_processor.get_block(get_random_value_hex(32))

    assert block is None


def test_get_block_count(block_processor):
    block_count = block_processor.get_block_count()
    assert isinstance(block_count, int) and block_count >= 0


def test_decode_raw_transaction(block_processor):
    # We cannot exhaustively test this (we rely on bitcoind for this) but we can try to decode a correct transaction
    assert block_processor.decode_raw_transaction(hex_tx) is not None


def test_decode_raw_transaction_invalid(block_processor):
    # Same but with an invalid one
    with pytest.raises(InvalidTransactionFormat):
        block_processor.decode_raw_transaction(hex_tx[::-1])


def test_get_missed_blocks(block_processor):
    target_block = block_processor.get_best_block_hash()

    # Generate some blocks and store the hash in a list
    missed_blocks = generate_blocks(5)

    # Check what we've missed
    assert block_processor.get_missed_blocks(target_block) == missed_blocks

    # We can see how it does not work if we replace the target by the first element in the list
    block_tip = missed_blocks[0]
    assert block_processor.get_missed_blocks(block_tip) != missed_blocks

    # But it does again if we skip that block
    assert block_processor.get_missed_blocks(block_tip) == missed_blocks[1:]


def test_get_distance_to_tip(block_processor):
    target_distance = 5

    target_block = block_processor.get_best_block_hash()

    # Mine some blocks up to the target distance
    generate_blocks(target_distance)

    # Check if the distance is properly computed
    assert block_processor.get_distance_to_tip(target_block) == target_distance


def test_is_block_in_best_chain(block_processor):
    best_block_hash = block_processor.get_best_block_hash()
    best_block = block_processor.get_block(best_block_hash)

    assert block_processor.is_block_in_best_chain(best_block_hash)

    fork(best_block.get("previousblockhash"), 2)

    assert not block_processor.is_block_in_best_chain(best_block_hash)


def test_find_last_common_ancestor(block_processor):
    ancestor = block_processor.get_best_block_hash()
    blocks = generate_blocks(3)
    best_block_hash = blocks[-1]

    # Create a fork (invalidate the next block after the ancestor and mine 4 blocks on top)
    fork(blocks[0], 4)

    # The last common ancestor between the old best and the new best should be the "ancestor"
    last_common_ancestor, dropped_txs = block_processor.find_last_common_ancestor(best_block_hash)
    assert last_common_ancestor == ancestor
    assert len(dropped_txs) == 3
