diff --git a/test/common/unit/test_tools.py b/test/common/unit/test_tools.py index 9b67f5e..4d11ee1 100644 --- a/test/common/unit/test_tools.py +++ b/test/common/unit/test_tools.py @@ -12,7 +12,7 @@ from common.tools import ( check_locator_format, compute_locator, setup_data_folder, - check_conf_fields, + create_config_dict, extend_paths, setup_logging, ) @@ -77,7 +77,7 @@ def test_setup_data_folder(): def test_check_conf_fields(): # The test should work with a valid config_fields (obtained from a valid conf.py) - assert type(check_conf_fields(conf_fields_copy)) == dict + assert type(create_config_dict(conf_fields_copy)) == dict def test_bad_check_conf_fields(): @@ -88,7 +88,7 @@ def test_bad_check_conf_fields(): # We should get a ValueError here. with pytest.raises(Exception): - check_conf_fields(conf_fields_copy) + create_config_dict(conf_fields_copy) def test_extend_paths(): diff --git a/test/teos/unit/conftest.py b/test/teos/unit/conftest.py index e9b0071..ebdff6f 100644 --- a/test/teos/unit/conftest.py +++ b/test/teos/unit/conftest.py @@ -1,32 +1,40 @@ -import os import pytest import random import requests from time import sleep from shutil import rmtree from threading import Thread - from coincurve import PrivateKey -from common.blob import Blob -from teos.responder import TransactionTracker -from teos.tools import bitcoin_cli -from teos.db_manager import DBManager -from common.appointment import Appointment -from common.tools import compute_locator - -from bitcoind_mock.transaction import create_dummy_transaction from bitcoind_mock.bitcoind import BitcoindMock from bitcoind_mock.conf import BTC_RPC_HOST, BTC_RPC_PORT +from bitcoind_mock.transaction import create_dummy_transaction + +from teos.carrier import Carrier +from teos.tools import bitcoin_cli +from teos.db_manager import DBManager +from teos import LOG_PREFIX, DEFAULT_CONF +from teos.responder import TransactionTracker +from teos.block_processor import BlockProcessor -from teos import LOG_PREFIX import common.cryptographer +from common.blob import Blob from common.logger import Logger +from common.tools import compute_locator +from common.appointment import Appointment from common.constants import LOCATOR_LEN_HEX +from common.config_loader import ConfigLoader from common.cryptographer import Cryptographer common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX) +# Set params to connect to regtest for testing +DEFAULT_CONF["BTC_RPC_PORT"]["value"] = 18443 +DEFAULT_CONF["BTC_NETWORK"]["value"] = "regtest" + +bitcoind_connect_params = {k: v["value"] for k, v in DEFAULT_CONF.items() if k.startswith("BTC")} +bitcoind_feed_params = {k: v["value"] for k, v in DEFAULT_CONF.items() if k.startswith("FEED")} + @pytest.fixture(scope="session") def run_bitcoind(): @@ -54,6 +62,16 @@ def db_manager(): rmtree("test_db") +@pytest.fixture(scope="module") +def carrier(): + return Carrier(bitcoind_connect_params) + + +@pytest.fixture(scope="module") +def block_processor(): + return BlockProcessor(bitcoind_connect_params) + + def generate_keypair(): sk = PrivateKey() pk = sk.public_key @@ -84,7 +102,7 @@ def fork(block_hash): def generate_dummy_appointment_data(real_height=True, start_time_offset=5, end_time_offset=30): if real_height: - current_height = bitcoin_cli().getblockcount() + current_height = bitcoin_cli(bitcoind_connect_params).getblockcount() else: current_height = 10 @@ -151,23 +169,7 @@ def generate_dummy_tracker(): def get_config(): - data_folder = os.path.expanduser("~/.teos") - config = { - "BTC_RPC_USER": "username", - "BTC_RPC_PASSWD": "password", - "BTC_RPC_HOST": "localhost", - "BTC_RPC_PORT": 8332, - "BTC_NETWORK": "regtest", - "FEED_PROTOCOL": "tcp", - "FEED_ADDR": "127.0.0.1", - "FEED_PORT": 28332, - "DATA_FOLDER": data_folder, - "MAX_APPOINTMENTS": 100, - "EXPIRY_DELTA": 6, - "MIN_TO_SELF_DELAY": 20, - "SERVER_LOG_FILE": data_folder + "teos.log", - "TEOS_SECRET_KEY": data_folder + "teos_sk.der", - "DB_PATH": "appointments", - } + config_loader = ConfigLoader(".", DEFAULT_CONF, {}) + config = config_loader.build_config() return config diff --git a/test/teos/unit/test_api.py b/test/teos/unit/test_api.py index 5b910b4..2273087 100644 --- a/test/teos/unit/test_api.py +++ b/test/teos/unit/test_api.py @@ -5,10 +5,11 @@ from time import sleep from threading import Thread from teos.api import API -from teos.watcher import Watcher -from teos.responder import Responder -from teos.tools import bitcoin_cli from teos import HOST, PORT +from teos.watcher import Watcher +from teos.tools import bitcoin_cli +from teos.inspector import Inspector +from teos.responder import Responder from teos.chain_monitor import ChainMonitor from test.teos.unit.conftest import ( @@ -18,8 +19,11 @@ from test.teos.unit.conftest import ( generate_dummy_appointment_data, generate_keypair, get_config, + bitcoind_connect_params, + bitcoind_feed_params, ) + from common.constants import LOCATOR_LEN_BYTES @@ -33,15 +37,21 @@ config = get_config() @pytest.fixture(scope="module") -def run_api(db_manager): +def run_api(db_manager, carrier, block_processor): sk, pk = generate_keypair() - watcher = Watcher(db_manager, Responder(db_manager), sk.to_der(), get_config()) - chain_monitor = ChainMonitor(watcher.block_queue, watcher.responder.block_queue) + responder = Responder(db_manager, carrier, block_processor) + watcher = Watcher( + db_manager, block_processor, responder, sk.to_der(), config.get("MAX_APPOINTMENTS"), config.get("EXPIRY_DELTA") + ) + + chain_monitor = ChainMonitor( + watcher.block_queue, watcher.responder.block_queue, block_processor, bitcoind_feed_params + ) watcher.awake() chain_monitor.monitor_chain() - api_thread = Thread(target=API(watcher, config).start) + api_thread = Thread(target=API(Inspector(block_processor, config.get("MIN_TO_SELF_DELAY")), watcher).start) api_thread.daemon = True api_thread.start() @@ -131,7 +141,7 @@ def test_get_all_appointments_responder(): locators = [appointment["locator"] for appointment in appointments] for locator, dispute_tx in locator_dispute_tx_map.items(): if locator in locators: - bitcoin_cli().sendrawtransaction(dispute_tx) + bitcoin_cli(bitcoind_connect_params).sendrawtransaction(dispute_tx) # Confirm transactions generate_blocks(6) @@ -173,7 +183,7 @@ def test_request_appointment_watcher(new_appt_data): def test_request_appointment_responder(new_appt_data): # Let's do something similar to what we did with the watcher but now we'll send the dispute tx to the network dispute_tx = locator_dispute_tx_map[new_appt_data["appointment"]["locator"]] - bitcoin_cli().sendrawtransaction(dispute_tx) + bitcoin_cli(bitcoind_connect_params).sendrawtransaction(dispute_tx) r = add_appointment(new_appt_data) assert r.status_code == 200 diff --git a/test/teos/unit/test_block_processor.py b/test/teos/unit/test_block_processor.py index f042423..abcb964 100644 --- a/test/teos/unit/test_block_processor.py +++ b/test/teos/unit/test_block_processor.py @@ -1,7 +1,6 @@ import pytest -from teos.block_processor import BlockProcessor -from test.teos.unit.conftest import get_random_value_hex, generate_block, generate_blocks, fork +from test.teos.unit.conftest import get_random_value_hex, generate_block, generate_blocks, fork, bitcoind_connect_params hex_tx = ( @@ -14,19 +13,16 @@ hex_tx = ( ) -@pytest.fixture -def best_block_hash(): - return BlockProcessor.get_best_block_hash() - - -def test_get_best_block_hash(run_bitcoind, best_block_hash): +def test_get_best_block_hash(run_bitcoind, block_processor): + best_block_hash = block_processor.get_best_block_hash() # As long as bitcoind is running (or mocked in this case) we should always a block hash assert best_block_hash is not None and isinstance(best_block_hash, str) -def test_get_block(best_block_hash): +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 = BlockProcessor.get_block(best_block_hash) + block = block_processor.get_block(best_block_hash) # Checking that the received block has at least the fields we need # FIXME: We could be more strict here, but we'll need to add those restrictions to bitcoind_sim too @@ -34,75 +30,75 @@ def test_get_block(best_block_hash): 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 = BlockProcessor.get_block(get_random_value_hex(32)) +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_count = BlockProcessor.get_block_count() +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(): +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 BlockProcessor.decode_raw_transaction(hex_tx) is not None + assert block_processor.decode_raw_transaction(hex_tx) is not None -def test_decode_raw_transaction_invalid(): +def test_decode_raw_transaction_invalid(block_processor): # Same but with an invalid one - assert BlockProcessor.decode_raw_transaction(hex_tx[::-1]) is None + assert block_processor.decode_raw_transaction(hex_tx[::-1]) is None -def test_get_missed_blocks(): - target_block = BlockProcessor.get_best_block_hash() +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 = [] for _ in range(5): generate_block() - missed_blocks.append(BlockProcessor.get_best_block_hash()) + missed_blocks.append(block_processor.get_best_block_hash()) # Check what we've missed - assert BlockProcessor.get_missed_blocks(target_block) == missed_blocks + 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 BlockProcessor.get_missed_blocks(block_tip) != missed_blocks + assert block_processor.get_missed_blocks(block_tip) != missed_blocks # But it does again if we skip that block - assert BlockProcessor.get_missed_blocks(block_tip) == missed_blocks[1:] + assert block_processor.get_missed_blocks(block_tip) == missed_blocks[1:] -def test_get_distance_to_tip(): +def test_get_distance_to_tip(block_processor): target_distance = 5 - target_block = BlockProcessor.get_best_block_hash() + 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 BlockProcessor.get_distance_to_tip(target_block) == target_distance + assert block_processor.get_distance_to_tip(target_block) == target_distance -def test_is_block_in_best_chain(): - best_block_hash = BlockProcessor.get_best_block_hash() - best_block = BlockProcessor.get_block(best_block_hash) +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 BlockProcessor.is_block_in_best_chain(best_block_hash) + assert block_processor.is_block_in_best_chain(best_block_hash) fork(best_block.get("previousblockhash")) generate_blocks(2) - assert not BlockProcessor.is_block_in_best_chain(best_block_hash) + assert not block_processor.is_block_in_best_chain(best_block_hash) -def test_find_last_common_ancestor(): - ancestor = BlockProcessor.get_best_block_hash() +def test_find_last_common_ancestor(block_processor): + ancestor = block_processor.get_best_block_hash() generate_blocks(3) - best_block_hash = BlockProcessor.get_best_block_hash() + best_block_hash = block_processor.get_best_block_hash() # Create a fork (forking creates a block if the mock is set by events) fork(ancestor) @@ -111,6 +107,6 @@ def test_find_last_common_ancestor(): generate_blocks(5) # The last common ancestor between the old best and the new best should be the "ancestor" - last_common_ancestor, dropped_txs = BlockProcessor.find_last_common_ancestor(best_block_hash) + last_common_ancestor, dropped_txs = block_processor.find_last_common_ancestor(best_block_hash) assert last_common_ancestor == ancestor assert len(dropped_txs) == 3 diff --git a/test/teos/unit/test_builder.py b/test/teos/unit/test_builder.py index c67616f..014d797 100644 --- a/test/teos/unit/test_builder.py +++ b/test/teos/unit/test_builder.py @@ -5,6 +5,7 @@ from queue import Queue from teos.builder import Builder from teos.watcher import Watcher from teos.responder import Responder + from test.teos.unit.conftest import ( get_random_value_hex, generate_dummy_appointment, @@ -12,8 +13,11 @@ from test.teos.unit.conftest import ( generate_block, bitcoin_cli, get_config, + bitcoind_connect_params, ) +config = get_config() + def test_build_appointments(): appointments_data = {} @@ -89,8 +93,15 @@ def test_populate_block_queue(): assert len(blocks) == 0 -def test_update_states_empty_list(db_manager): - w = Watcher(db_manager=db_manager, responder=Responder(db_manager), sk_der=None, config=None) +def test_update_states_empty_list(db_manager, carrier, block_processor): + w = Watcher( + db_manager=db_manager, + block_processor=block_processor, + responder=Responder(db_manager, carrier, block_processor), + sk_der=None, + max_appointments=config.get("MAX_APPOINTMENTS"), + expiry_delta=config.get("EXPIRY_DELTA"), + ) missed_blocks_watcher = [] missed_blocks_responder = [get_random_value_hex(32)] @@ -103,13 +114,20 @@ def test_update_states_empty_list(db_manager): Builder.update_states(w, missed_blocks_responder, missed_blocks_watcher) -def test_update_states_responder_misses_more(run_bitcoind, db_manager): - w = Watcher(db_manager=db_manager, responder=Responder(db_manager), sk_der=None, config=get_config()) +def test_update_states_responder_misses_more(run_bitcoind, db_manager, carrier, block_processor): + w = Watcher( + db_manager=db_manager, + block_processor=block_processor, + responder=Responder(db_manager, carrier, block_processor), + sk_der=None, + max_appointments=config.get("MAX_APPOINTMENTS"), + expiry_delta=config.get("EXPIRY_DELTA"), + ) blocks = [] for _ in range(5): generate_block() - blocks.append(bitcoin_cli().getbestblockhash()) + blocks.append(bitcoin_cli(bitcoind_connect_params).getbestblockhash()) # Updating the states should bring both to the same last known block. w.awake() @@ -120,14 +138,21 @@ def test_update_states_responder_misses_more(run_bitcoind, db_manager): assert w.responder.last_known_block == blocks[-1] -def test_update_states_watcher_misses_more(run_bitcoind, db_manager): +def test_update_states_watcher_misses_more(db_manager, carrier, block_processor): # Same as before, but data is now in the Responder - w = Watcher(db_manager=db_manager, responder=Responder(db_manager), sk_der=None, config=get_config()) + w = Watcher( + db_manager=db_manager, + block_processor=block_processor, + responder=Responder(db_manager, carrier, block_processor), + sk_der=None, + max_appointments=config.get("MAX_APPOINTMENTS"), + expiry_delta=config.get("EXPIRY_DELTA"), + ) blocks = [] for _ in range(5): generate_block() - blocks.append(bitcoin_cli().getbestblockhash()) + blocks.append(bitcoin_cli(bitcoind_connect_params).getbestblockhash()) w.awake() w.responder.awake() diff --git a/test/teos/unit/test_carrier.py b/test/teos/unit/test_carrier.py index d8e6ab6..01c363e 100644 --- a/test/teos/unit/test_carrier.py +++ b/test/teos/unit/test_carrier.py @@ -1,6 +1,3 @@ -import pytest - -from teos.carrier import Carrier from bitcoind_mock.transaction import create_dummy_transaction from test.teos.unit.conftest import generate_blocks, get_random_value_hex from teos.rpc_errors import RPC_VERIFY_ALREADY_IN_CHAIN, RPC_DESERIALIZATION_ERROR @@ -14,11 +11,6 @@ from teos.rpc_errors import RPC_VERIFY_ALREADY_IN_CHAIN, RPC_DESERIALIZATION_ERR sent_txs = [] -@pytest.fixture(scope="module") -def carrier(): - return Carrier() - - def test_send_transaction(run_bitcoind, carrier): tx = create_dummy_transaction() @@ -56,15 +48,15 @@ def test_send_transaction_invalid_format(carrier): assert receipt.delivered is False and receipt.reason == RPC_DESERIALIZATION_ERROR -def test_get_transaction(): +def test_get_transaction(carrier): # We should be able to get back every transaction we've sent for tx in sent_txs: - tx_info = Carrier.get_transaction(tx) + tx_info = carrier.get_transaction(tx) assert tx_info is not None -def test_get_non_existing_transaction(): - tx_info = Carrier.get_transaction(get_random_value_hex(32)) +def test_get_non_existing_transaction(carrier): + tx_info = carrier.get_transaction(get_random_value_hex(32)) assert tx_info is None diff --git a/test/teos/unit/test_chain_monitor.py b/test/teos/unit/test_chain_monitor.py index d570c71..c0d969b 100644 --- a/test/teos/unit/test_chain_monitor.py +++ b/test/teos/unit/test_chain_monitor.py @@ -3,17 +3,16 @@ import time from queue import Queue from threading import Thread, Event, Condition -from teos.block_processor import BlockProcessor from teos.chain_monitor import ChainMonitor -from test.teos.unit.conftest import get_random_value_hex, generate_block +from test.teos.unit.conftest import get_random_value_hex, generate_block, bitcoind_connect_params, bitcoind_feed_params -def test_init(run_bitcoind): +def test_init(run_bitcoind, block_processor): # run_bitcoind is started here instead of later on to avoid race conditions while it initializes # Not much to test here, just sanity checks to make sure nothing goes south in the future - chain_monitor = ChainMonitor(Queue(), Queue()) + chain_monitor = ChainMonitor(Queue(), Queue(), block_processor, bitcoind_feed_params) assert chain_monitor.best_tip is None assert isinstance(chain_monitor.last_tips, list) and len(chain_monitor.last_tips) == 0 @@ -27,8 +26,8 @@ def test_init(run_bitcoind): assert isinstance(chain_monitor.responder_queue, Queue) -def test_notify_subscribers(): - chain_monitor = ChainMonitor(Queue(), Queue()) +def test_notify_subscribers(block_processor): + chain_monitor = ChainMonitor(Queue(), Queue(), block_processor, bitcoind_feed_params) # Subscribers are only notified as long as they are awake new_block = get_random_value_hex(32) @@ -42,11 +41,11 @@ def test_notify_subscribers(): assert chain_monitor.responder_queue.get() == new_block -def test_update_state(): +def test_update_state(block_processor): # The state is updated after receiving a new block (and only if the block is not already known). # Let's start by setting a best_tip and a couple of old tips new_block_hash = get_random_value_hex(32) - chain_monitor = ChainMonitor(Queue(), Queue()) + chain_monitor = ChainMonitor(Queue(), Queue(), block_processor, bitcoind_feed_params) chain_monitor.best_tip = new_block_hash chain_monitor.last_tips = [get_random_value_hex(32) for _ in range(5)] @@ -63,14 +62,15 @@ def test_update_state(): assert chain_monitor.best_tip == another_block_hash and new_block_hash == chain_monitor.last_tips[-1] -def test_monitor_chain_polling(db_manager): +def test_monitor_chain_polling(db_manager, block_processor): # Try polling with the Watcher wq = Queue() - chain_monitor = ChainMonitor(wq, Queue()) - chain_monitor.best_tip = BlockProcessor.get_best_block_hash() + chain_monitor = ChainMonitor(Queue(), Queue(), block_processor, bitcoind_feed_params) + chain_monitor.best_tip = block_processor.get_best_block_hash() + chain_monitor.polling_delta = 0.1 # monitor_chain_polling runs until terminate if set - polling_thread = Thread(target=chain_monitor.monitor_chain_polling, kwargs={"polling_delta": 0.1}, daemon=True) + polling_thread = Thread(target=chain_monitor.monitor_chain_polling, daemon=True) polling_thread.start() # Check that nothing changes as long as a block is not generated @@ -88,10 +88,10 @@ def test_monitor_chain_polling(db_manager): polling_thread.join() -def test_monitor_chain_zmq(db_manager): - rq = Queue() - chain_monitor = ChainMonitor(Queue(), rq) - chain_monitor.best_tip = BlockProcessor.get_best_block_hash() +def test_monitor_chain_zmq(db_manager, block_processor): + responder_queue = Queue() + chain_monitor = ChainMonitor(Queue(), responder_queue, block_processor, bitcoind_feed_params) + chain_monitor.best_tip = block_processor.get_best_block_hash() zmq_thread = Thread(target=chain_monitor.monitor_chain_zmq, daemon=True) zmq_thread.start() @@ -106,9 +106,9 @@ def test_monitor_chain_zmq(db_manager): assert chain_monitor.responder_queue.empty() -def test_monitor_chain(db_manager): +def test_monitor_chain(db_manager, block_processor): # Not much to test here, this should launch two threads (one per monitor approach) and finish on terminate - chain_monitor = ChainMonitor(Queue(), Queue()) + chain_monitor = ChainMonitor(Queue(), Queue(), block_processor, bitcoind_feed_params) chain_monitor.best_tip = None chain_monitor.monitor_chain() @@ -131,15 +131,16 @@ def test_monitor_chain(db_manager): generate_block() -def test_monitor_chain_single_update(db_manager): +def test_monitor_chain_single_update(db_manager, block_processor): # This test tests that if both threads try to add the same block to the queue, only the first one will make it - chain_monitor = ChainMonitor(Queue(), Queue()) + chain_monitor = ChainMonitor(Queue(), Queue(), block_processor, bitcoind_feed_params) chain_monitor.best_tip = None + chain_monitor.polling_delta = 2 # We will create a block and wait for the polling thread. Then check the queues to see that the block hash has only # been added once. - chain_monitor.monitor_chain(polling_delta=2) + chain_monitor.monitor_chain() generate_block() watcher_block = chain_monitor.watcher_queue.get() diff --git a/test/teos/unit/test_inspector.py b/test/teos/unit/test_inspector.py index 9fba261..d0c35ec 100644 --- a/test/teos/unit/test_inspector.py +++ b/test/teos/unit/test_inspector.py @@ -1,26 +1,27 @@ from binascii import unhexlify from teos.errors import * -from teos.inspector import Inspector -from common.appointment import Appointment -from teos.block_processor import BlockProcessor -from teos.conf import MIN_TO_SELF_DELAY - -from test.teos.unit.conftest import get_random_value_hex, generate_dummy_appointment_data, generate_keypair, get_config - -from common.constants import LOCATOR_LEN_BYTES, LOCATOR_LEN_HEX -from common.cryptographer import Cryptographer -from common.logger import Logger - from teos import LOG_PREFIX +from teos.inspector import Inspector +from teos.block_processor import BlockProcessor + import common.cryptographer +from common.logger import Logger +from common.appointment import Appointment +from common.cryptographer import Cryptographer +from common.constants import LOCATOR_LEN_BYTES, LOCATOR_LEN_HEX + +from test.teos.unit.conftest import ( + get_random_value_hex, + generate_dummy_appointment_data, + generate_keypair, + bitcoind_connect_params, + get_config, +) common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX) - -inspector = Inspector(get_config()) APPOINTMENT_OK = (0, None) - NO_HEX_STRINGS = [ "R" * LOCATOR_LEN_HEX, get_random_value_hex(LOCATOR_LEN_BYTES - 1) + "PP", @@ -41,6 +42,11 @@ WRONG_TYPES = [ ] WRONG_TYPES_NO_STR = [[], unhexlify(get_random_value_hex(LOCATOR_LEN_BYTES)), 3.2, 2.0, (), object, {}, object()] +config = get_config() +MIN_TO_SELF_DELAY = config.get("MIN_TO_SELF_DELAY") +block_processor = BlockProcessor(bitcoind_connect_params) +inspector = Inspector(block_processor, MIN_TO_SELF_DELAY) + def test_check_locator(): # Right appointment type, size and format @@ -200,7 +206,7 @@ def test_inspect(run_bitcoind): # Valid appointment locator = get_random_value_hex(LOCATOR_LEN_BYTES) - start_time = BlockProcessor.get_block_count() + 5 + start_time = block_processor.get_block_count() + 5 end_time = start_time + 20 to_self_delay = MIN_TO_SELF_DELAY encrypted_blob = get_random_value_hex(64) diff --git a/test/teos/unit/test_responder.py b/test/teos/unit/test_responder.py index 115ad41..c667cc0 100644 --- a/test/teos/unit/test_responder.py +++ b/test/teos/unit/test_responder.py @@ -1,27 +1,33 @@ import json import pytest import random -from queue import Queue from uuid import uuid4 +from queue import Queue from shutil import rmtree from copy import deepcopy from threading import Thread -from teos.db_manager import DBManager -from teos.responder import Responder, TransactionTracker -from teos.block_processor import BlockProcessor -from teos.chain_monitor import ChainMonitor +from teos.carrier import Carrier from teos.tools import bitcoin_cli +from teos.db_manager import DBManager +from teos.chain_monitor import ChainMonitor +from teos.responder import Responder, TransactionTracker from common.constants import LOCATOR_LEN_HEX from bitcoind_mock.transaction import create_dummy_transaction, create_tx_from_hex -from test.teos.unit.conftest import generate_block, generate_blocks, get_random_value_hex +from test.teos.unit.conftest import ( + generate_block, + generate_blocks, + get_random_value_hex, + bitcoind_connect_params, + bitcoind_feed_params, +) @pytest.fixture(scope="module") -def responder(db_manager): - responder = Responder(db_manager) - chain_monitor = ChainMonitor(Queue(), responder.block_queue) +def responder(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() return responder @@ -61,7 +67,7 @@ def create_dummy_tracker_data(random_txid=False, penalty_rawtx=None): if random_txid is True: penalty_txid = get_random_value_hex(32) - appointment_end = bitcoin_cli().getblockcount() + 2 + appointment_end = bitcoin_cli(bitcoind_connect_params).getblockcount() + 2 locator = dispute_txid[:LOCATOR_LEN_HEX] return locator, dispute_txid, penalty_txid, penalty_rawtx, appointment_end @@ -86,21 +92,21 @@ def test_tracker_init(run_bitcoind): ) -def test_on_sync(run_bitcoind, responder): +def test_on_sync(run_bitcoind, responder, block_processor): # We're on sync if we're 1 or less blocks behind the tip - chain_tip = BlockProcessor.get_best_block_hash() - assert Responder.on_sync(chain_tip) is True + chain_tip = block_processor.get_best_block_hash() + assert responder.on_sync(chain_tip) is True generate_block() - assert Responder.on_sync(chain_tip) is True + assert responder.on_sync(chain_tip) is True -def test_on_sync_fail(responder): +def test_on_sync_fail(responder, block_processor): # This should fail if we're more than 1 block behind the tip - chain_tip = BlockProcessor.get_best_block_hash() + chain_tip = block_processor.get_best_block_hash() generate_blocks(2) - assert Responder.on_sync(chain_tip) is False + assert responder.on_sync(chain_tip) is False def test_tracker_to_dict(): @@ -147,8 +153,8 @@ def test_tracker_from_dict_invalid_data(): assert True -def test_init_responder(temp_db_manager): - responder = Responder(temp_db_manager) +def test_init_responder(temp_db_manager, carrier, block_processor): + responder = Responder(temp_db_manager, carrier, block_processor) assert isinstance(responder.trackers, dict) and len(responder.trackers) == 0 assert isinstance(responder.tx_tracker_map, dict) and len(responder.tx_tracker_map) == 0 assert isinstance(responder.unconfirmed_txs, list) and len(responder.unconfirmed_txs) == 0 @@ -156,8 +162,8 @@ def test_init_responder(temp_db_manager): assert responder.block_queue.empty() -def test_handle_breach(db_manager): - responder = Responder(db_manager) +def test_handle_breach(db_manager, carrier, block_processor): + responder = Responder(db_manager, carrier, block_processor) uuid = uuid4().hex tracker = create_dummy_tracker() @@ -176,7 +182,11 @@ def test_handle_breach(db_manager): assert receipt.delivered is True -def test_handle_breach_bad_response(responder): +def test_handle_breach_bad_response(db_manager, block_processor): + # We need a new carrier here, otherwise the transaction will be flagged as previously sent and receipt.delivered + # will be True + responder = Responder(db_manager, Carrier(bitcoind_connect_params), block_processor) + uuid = uuid4().hex tracker = create_dummy_tracker() @@ -262,10 +272,10 @@ def test_add_tracker_already_confirmed(responder): assert penalty_txid not in responder.unconfirmed_txs -def test_do_watch(temp_db_manager): +def test_do_watch(temp_db_manager, carrier, block_processor): # Create a fresh responder to simplify the test - responder = Responder(temp_db_manager) - chain_monitor = ChainMonitor(Queue(), responder.block_queue) + 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)] @@ -293,7 +303,7 @@ def test_do_watch(temp_db_manager): # And broadcast some of the transactions broadcast_txs = [] for tracker in trackers[:5]: - bitcoin_cli().sendrawtransaction(tracker.penalty_rawtx) + bitcoin_cli(bitcoind_connect_params).sendrawtransaction(tracker.penalty_rawtx) broadcast_txs.append(tracker.penalty_txid) # Mine a block @@ -312,7 +322,7 @@ def test_do_watch(temp_db_manager): # Do the rest broadcast_txs = [] for tracker in trackers[5:]: - bitcoin_cli().sendrawtransaction(tracker.penalty_rawtx) + bitcoin_cli(bitcoind_connect_params).sendrawtransaction(tracker.penalty_rawtx) broadcast_txs.append(tracker.penalty_txid) # Mine a block @@ -321,9 +331,9 @@ def test_do_watch(temp_db_manager): assert len(responder.tx_tracker_map) == 0 -def test_check_confirmations(db_manager): - responder = Responder(db_manager) - chain_monitor = ChainMonitor(Queue(), responder.block_queue) +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 @@ -378,11 +388,11 @@ def test_get_txs_to_rebroadcast(responder): assert txs_to_rebroadcast == list(txs_missing_too_many_conf.keys()) -def test_get_completed_trackers(db_manager): - initial_height = bitcoin_cli().getblockcount() +def test_get_completed_trackers(db_manager, carrier, block_processor): + initial_height = bitcoin_cli(bitcoind_connect_params).getblockcount() - responder = Responder(db_manager) - chain_monitor = ChainMonitor(Queue(), responder.block_queue) + 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) @@ -417,7 +427,7 @@ def test_get_completed_trackers(db_manager): } for uuid, tracker in all_trackers.items(): - bitcoin_cli().sendrawtransaction(tracker.penalty_rawtx) + 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) @@ -438,9 +448,9 @@ def test_get_completed_trackers(db_manager): assert set(completed_trackers_ids) == set(ended_trackers_keys) -def test_rebroadcast(db_manager): - responder = Responder(db_manager) - chain_monitor = ChainMonitor(Queue(), responder.block_queue) +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 = [] diff --git a/test/teos/unit/test_tools.py b/test/teos/unit/test_tools.py index 77f431d..45bceab 100644 --- a/test/teos/unit/test_tools.py +++ b/test/teos/unit/test_tools.py @@ -1,17 +1,17 @@ from teos.tools import can_connect_to_bitcoind, in_correct_network, bitcoin_cli - from common.tools import check_sha256_hex_format +from test.teos.unit.conftest import bitcoind_connect_params def test_in_correct_network(run_bitcoind): # The simulator runs as if it was regtest, so every other network should fail - assert in_correct_network("mainnet") is False - assert in_correct_network("testnet") is False - assert in_correct_network("regtest") is True + assert in_correct_network(bitcoind_connect_params, "mainnet") is False + assert in_correct_network(bitcoind_connect_params, "testnet") is False + assert in_correct_network(bitcoind_connect_params, "regtest") is True def test_can_connect_to_bitcoind(): - assert can_connect_to_bitcoind() is True + assert can_connect_to_bitcoind(bitcoind_connect_params) is True # def test_can_connect_to_bitcoind_bitcoin_not_running(): @@ -22,7 +22,7 @@ def test_can_connect_to_bitcoind(): def test_bitcoin_cli(): try: - bitcoin_cli().help() + bitcoin_cli(bitcoind_connect_params).help() assert True except Exception: diff --git a/test/teos/unit/test_watcher.py b/test/teos/unit/test_watcher.py index 6f8e72d..de72298 100644 --- a/test/teos/unit/test_watcher.py +++ b/test/teos/unit/test_watcher.py @@ -4,11 +4,19 @@ from shutil import rmtree from threading import Thread from coincurve import PrivateKey +from teos import LOG_PREFIX +from teos.carrier import Carrier from teos.watcher import Watcher -from teos.responder import Responder from teos.tools import bitcoin_cli -from teos.chain_monitor import ChainMonitor +from teos.responder import Responder from teos.db_manager import DBManager +from teos.chain_monitor import ChainMonitor +from teos.block_processor import BlockProcessor + +import common.cryptographer +from common.logger import Logger +from common.tools import compute_locator +from common.cryptographer import Cryptographer from test.teos.unit.conftest import ( generate_blocks, @@ -16,14 +24,9 @@ from test.teos.unit.conftest import ( get_random_value_hex, generate_keypair, get_config, + bitcoind_feed_params, + bitcoind_connect_params, ) -from teos.conf import EXPIRY_DELTA, MAX_APPOINTMENTS - -import common.cryptographer -from teos import LOG_PREFIX -from common.logger import Logger -from common.tools import compute_locator -from common.cryptographer import Cryptographer common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX) @@ -33,6 +36,7 @@ START_TIME_OFFSET = 1 END_TIME_OFFSET = 1 TEST_SET_SIZE = 200 +config = get_config() signing_key, public_key = generate_keypair() @@ -50,8 +54,22 @@ def temp_db_manager(): @pytest.fixture(scope="module") def watcher(db_manager): - watcher = Watcher(db_manager, Responder(db_manager), signing_key.to_der(), get_config()) - chain_monitor = ChainMonitor(watcher.block_queue, watcher.responder.block_queue) + block_processor = BlockProcessor(bitcoind_connect_params) + carrier = Carrier(bitcoind_connect_params) + + responder = Responder(db_manager, carrier, block_processor) + watcher = Watcher( + db_manager, + block_processor, + responder, + signing_key.to_der(), + config.get("MAX_APPOINTMENTS"), + config.get("EXPIRY_DELTA"), + ) + + chain_monitor = ChainMonitor( + watcher.block_queue, watcher.responder.block_queue, block_processor, bitcoind_feed_params + ) chain_monitor.monitor_chain() return watcher @@ -89,9 +107,11 @@ def test_init(run_bitcoind, watcher): assert isinstance(watcher.appointments, dict) and len(watcher.appointments) == 0 assert isinstance(watcher.locator_uuid_map, dict) and len(watcher.locator_uuid_map) == 0 assert watcher.block_queue.empty() - assert isinstance(watcher.config, dict) - assert isinstance(watcher.signing_key, PrivateKey) + assert isinstance(watcher.block_processor, BlockProcessor) assert isinstance(watcher.responder, Responder) + assert isinstance(watcher.max_appointments, int) + assert isinstance(watcher.expiry_delta, int) + assert isinstance(watcher.signing_key, PrivateKey) def test_add_appointment(watcher): @@ -120,7 +140,7 @@ def test_add_too_many_appointments(watcher): # Any appointment on top of those should fail watcher.appointments = dict() - for _ in range(MAX_APPOINTMENTS): + for _ in range(config.get("MAX_APPOINTMENTS")): appointment, dispute_tx = generate_dummy_appointment( start_time_offset=START_TIME_OFFSET, end_time_offset=END_TIME_OFFSET ) @@ -160,7 +180,7 @@ def test_do_watch(watcher, temp_db_manager): # Broadcast the first two for dispute_tx in dispute_txs[:2]: - bitcoin_cli().sendrawtransaction(dispute_tx) + bitcoin_cli(bitcoind_connect_params).sendrawtransaction(dispute_tx) # After generating enough blocks, the number of appointments should have reduced by two generate_blocks(START_TIME_OFFSET + END_TIME_OFFSET) @@ -169,7 +189,7 @@ def test_do_watch(watcher, temp_db_manager): # The rest of appointments will timeout after the end (2) + EXPIRY_DELTA # Wait for an additional block to be safe - generate_blocks(EXPIRY_DELTA + START_TIME_OFFSET + END_TIME_OFFSET) + generate_blocks(config.get("EXPIRY_DELTA") + START_TIME_OFFSET + END_TIME_OFFSET) assert len(watcher.appointments) == 0