Refactors the tests to match the new class definitions and config formats

This commit is contained in:
Sergi Delgado Segura
2020-03-20 21:20:24 +01:00
parent b43731397c
commit cb79e51d09
11 changed files with 256 additions and 194 deletions

View File

@@ -12,7 +12,7 @@ from common.tools import (
check_locator_format, check_locator_format,
compute_locator, compute_locator,
setup_data_folder, setup_data_folder,
check_conf_fields, create_config_dict,
extend_paths, extend_paths,
setup_logging, setup_logging,
) )
@@ -77,7 +77,7 @@ def test_setup_data_folder():
def test_check_conf_fields(): def test_check_conf_fields():
# The test should work with a valid config_fields (obtained from a valid conf.py) # 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(): def test_bad_check_conf_fields():
@@ -88,7 +88,7 @@ def test_bad_check_conf_fields():
# We should get a ValueError here. # We should get a ValueError here.
with pytest.raises(Exception): with pytest.raises(Exception):
check_conf_fields(conf_fields_copy) create_config_dict(conf_fields_copy)
def test_extend_paths(): def test_extend_paths():

View File

@@ -1,32 +1,40 @@
import os
import pytest import pytest
import random import random
import requests import requests
from time import sleep from time import sleep
from shutil import rmtree from shutil import rmtree
from threading import Thread from threading import Thread
from coincurve import PrivateKey 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.bitcoind import BitcoindMock
from bitcoind_mock.conf import BTC_RPC_HOST, BTC_RPC_PORT 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 import common.cryptographer
from common.blob import Blob
from common.logger import Logger 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.constants import LOCATOR_LEN_HEX
from common.config_loader import ConfigLoader
from common.cryptographer import Cryptographer from common.cryptographer import Cryptographer
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX) 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") @pytest.fixture(scope="session")
def run_bitcoind(): def run_bitcoind():
@@ -54,6 +62,16 @@ def db_manager():
rmtree("test_db") 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(): def generate_keypair():
sk = PrivateKey() sk = PrivateKey()
pk = sk.public_key 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): def generate_dummy_appointment_data(real_height=True, start_time_offset=5, end_time_offset=30):
if real_height: if real_height:
current_height = bitcoin_cli().getblockcount() current_height = bitcoin_cli(bitcoind_connect_params).getblockcount()
else: else:
current_height = 10 current_height = 10
@@ -151,23 +169,7 @@ def generate_dummy_tracker():
def get_config(): def get_config():
data_folder = os.path.expanduser("~/.teos") config_loader = ConfigLoader(".", DEFAULT_CONF, {})
config = { config = config_loader.build_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",
}
return config return config

View File

@@ -5,10 +5,11 @@ from time import sleep
from threading import Thread from threading import Thread
from teos.api import API 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 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 teos.chain_monitor import ChainMonitor
from test.teos.unit.conftest import ( from test.teos.unit.conftest import (
@@ -18,8 +19,11 @@ from test.teos.unit.conftest import (
generate_dummy_appointment_data, generate_dummy_appointment_data,
generate_keypair, generate_keypair,
get_config, get_config,
bitcoind_connect_params,
bitcoind_feed_params,
) )
from common.constants import LOCATOR_LEN_BYTES from common.constants import LOCATOR_LEN_BYTES
@@ -33,15 +37,21 @@ config = get_config()
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def run_api(db_manager): def run_api(db_manager, carrier, block_processor):
sk, pk = generate_keypair() sk, pk = generate_keypair()
watcher = Watcher(db_manager, Responder(db_manager), sk.to_der(), get_config()) responder = Responder(db_manager, carrier, block_processor)
chain_monitor = ChainMonitor(watcher.block_queue, watcher.responder.block_queue) 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() watcher.awake()
chain_monitor.monitor_chain() 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.daemon = True
api_thread.start() api_thread.start()
@@ -131,7 +141,7 @@ def test_get_all_appointments_responder():
locators = [appointment["locator"] for appointment in appointments] locators = [appointment["locator"] for appointment in appointments]
for locator, dispute_tx in locator_dispute_tx_map.items(): for locator, dispute_tx in locator_dispute_tx_map.items():
if locator in locators: if locator in locators:
bitcoin_cli().sendrawtransaction(dispute_tx) bitcoin_cli(bitcoind_connect_params).sendrawtransaction(dispute_tx)
# Confirm transactions # Confirm transactions
generate_blocks(6) generate_blocks(6)
@@ -173,7 +183,7 @@ def test_request_appointment_watcher(new_appt_data):
def test_request_appointment_responder(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 # 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"]] 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) r = add_appointment(new_appt_data)
assert r.status_code == 200 assert r.status_code == 200

View File

@@ -1,7 +1,6 @@
import pytest import pytest
from teos.block_processor import BlockProcessor from test.teos.unit.conftest import get_random_value_hex, generate_block, generate_blocks, fork, bitcoind_connect_params
from test.teos.unit.conftest import get_random_value_hex, generate_block, generate_blocks, fork
hex_tx = ( hex_tx = (
@@ -14,19 +13,16 @@ hex_tx = (
) )
@pytest.fixture def test_get_best_block_hash(run_bitcoind, block_processor):
def best_block_hash(): best_block_hash = block_processor.get_best_block_hash()
return BlockProcessor.get_best_block_hash()
def test_get_best_block_hash(run_bitcoind, best_block_hash):
# As long as bitcoind is running (or mocked in this case) we should always a 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) 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 # 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 # 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 # 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 assert block.get("hash") == best_block_hash and "height" in block and "previousblockhash" in block and "tx" in block
def test_get_random_block(): def test_get_random_block(block_processor):
block = BlockProcessor.get_block(get_random_value_hex(32)) block = block_processor.get_block(get_random_value_hex(32))
assert block is None assert block is None
def test_get_block_count(): def test_get_block_count(block_processor):
block_count = BlockProcessor.get_block_count() block_count = block_processor.get_block_count()
assert isinstance(block_count, int) and block_count >= 0 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 # 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 # 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(): def test_get_missed_blocks(block_processor):
target_block = BlockProcessor.get_best_block_hash() target_block = block_processor.get_best_block_hash()
# Generate some blocks and store the hash in a list # Generate some blocks and store the hash in a list
missed_blocks = [] missed_blocks = []
for _ in range(5): for _ in range(5):
generate_block() generate_block()
missed_blocks.append(BlockProcessor.get_best_block_hash()) missed_blocks.append(block_processor.get_best_block_hash())
# Check what we've missed # 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 # 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] 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 # 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_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 # Mine some blocks up to the target distance
generate_blocks(target_distance) generate_blocks(target_distance)
# Check if the distance is properly computed # 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(): def test_is_block_in_best_chain(block_processor):
best_block_hash = BlockProcessor.get_best_block_hash() best_block_hash = block_processor.get_best_block_hash()
best_block = BlockProcessor.get_block(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")) fork(best_block.get("previousblockhash"))
generate_blocks(2) 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(): def test_find_last_common_ancestor(block_processor):
ancestor = BlockProcessor.get_best_block_hash() ancestor = block_processor.get_best_block_hash()
generate_blocks(3) 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) # Create a fork (forking creates a block if the mock is set by events)
fork(ancestor) fork(ancestor)
@@ -111,6 +107,6 @@ def test_find_last_common_ancestor():
generate_blocks(5) generate_blocks(5)
# The last common ancestor between the old best and the new best should be the "ancestor" # 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 last_common_ancestor == ancestor
assert len(dropped_txs) == 3 assert len(dropped_txs) == 3

View File

@@ -5,6 +5,7 @@ from queue import Queue
from teos.builder import Builder from teos.builder import Builder
from teos.watcher import Watcher from teos.watcher import Watcher
from teos.responder import Responder from teos.responder import Responder
from test.teos.unit.conftest import ( from test.teos.unit.conftest import (
get_random_value_hex, get_random_value_hex,
generate_dummy_appointment, generate_dummy_appointment,
@@ -12,8 +13,11 @@ from test.teos.unit.conftest import (
generate_block, generate_block,
bitcoin_cli, bitcoin_cli,
get_config, get_config,
bitcoind_connect_params,
) )
config = get_config()
def test_build_appointments(): def test_build_appointments():
appointments_data = {} appointments_data = {}
@@ -89,8 +93,15 @@ def test_populate_block_queue():
assert len(blocks) == 0 assert len(blocks) == 0
def test_update_states_empty_list(db_manager): def test_update_states_empty_list(db_manager, carrier, block_processor):
w = Watcher(db_manager=db_manager, responder=Responder(db_manager), sk_der=None, config=None) 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_watcher = []
missed_blocks_responder = [get_random_value_hex(32)] 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) Builder.update_states(w, missed_blocks_responder, missed_blocks_watcher)
def test_update_states_responder_misses_more(run_bitcoind, db_manager): def test_update_states_responder_misses_more(run_bitcoind, db_manager, carrier, block_processor):
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 = [] blocks = []
for _ in range(5): for _ in range(5):
generate_block() 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. # Updating the states should bring both to the same last known block.
w.awake() 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] 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 # 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 = [] blocks = []
for _ in range(5): for _ in range(5):
generate_block() generate_block()
blocks.append(bitcoin_cli().getbestblockhash()) blocks.append(bitcoin_cli(bitcoind_connect_params).getbestblockhash())
w.awake() w.awake()
w.responder.awake() w.responder.awake()

View File

@@ -1,6 +1,3 @@
import pytest
from teos.carrier import Carrier
from bitcoind_mock.transaction import create_dummy_transaction from bitcoind_mock.transaction import create_dummy_transaction
from test.teos.unit.conftest import generate_blocks, get_random_value_hex 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 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 = [] sent_txs = []
@pytest.fixture(scope="module")
def carrier():
return Carrier()
def test_send_transaction(run_bitcoind, carrier): def test_send_transaction(run_bitcoind, carrier):
tx = create_dummy_transaction() 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 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 # We should be able to get back every transaction we've sent
for tx in sent_txs: for tx in sent_txs:
tx_info = Carrier.get_transaction(tx) tx_info = carrier.get_transaction(tx)
assert tx_info is not None assert tx_info is not None
def test_get_non_existing_transaction(): def test_get_non_existing_transaction(carrier):
tx_info = Carrier.get_transaction(get_random_value_hex(32)) tx_info = carrier.get_transaction(get_random_value_hex(32))
assert tx_info is None assert tx_info is None

View File

@@ -3,17 +3,16 @@ import time
from queue import Queue from queue import Queue
from threading import Thread, Event, Condition from threading import Thread, Event, Condition
from teos.block_processor import BlockProcessor
from teos.chain_monitor import ChainMonitor 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 # 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 # 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 chain_monitor.best_tip is None
assert isinstance(chain_monitor.last_tips, list) and len(chain_monitor.last_tips) == 0 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) assert isinstance(chain_monitor.responder_queue, Queue)
def test_notify_subscribers(): def test_notify_subscribers(block_processor):
chain_monitor = ChainMonitor(Queue(), Queue()) chain_monitor = ChainMonitor(Queue(), Queue(), block_processor, bitcoind_feed_params)
# Subscribers are only notified as long as they are awake # Subscribers are only notified as long as they are awake
new_block = get_random_value_hex(32) new_block = get_random_value_hex(32)
@@ -42,11 +41,11 @@ def test_notify_subscribers():
assert chain_monitor.responder_queue.get() == new_block 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). # 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 # Let's start by setting a best_tip and a couple of old tips
new_block_hash = get_random_value_hex(32) 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.best_tip = new_block_hash
chain_monitor.last_tips = [get_random_value_hex(32) for _ in range(5)] 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] 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 # Try polling with the Watcher
wq = Queue() wq = Queue()
chain_monitor = ChainMonitor(wq, Queue()) chain_monitor = ChainMonitor(Queue(), Queue(), block_processor, bitcoind_feed_params)
chain_monitor.best_tip = BlockProcessor.get_best_block_hash() chain_monitor.best_tip = block_processor.get_best_block_hash()
chain_monitor.polling_delta = 0.1
# monitor_chain_polling runs until terminate if set # 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() polling_thread.start()
# Check that nothing changes as long as a block is not generated # 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() polling_thread.join()
def test_monitor_chain_zmq(db_manager): def test_monitor_chain_zmq(db_manager, block_processor):
rq = Queue() responder_queue = Queue()
chain_monitor = ChainMonitor(Queue(), rq) chain_monitor = ChainMonitor(Queue(), responder_queue, block_processor, bitcoind_feed_params)
chain_monitor.best_tip = BlockProcessor.get_best_block_hash() chain_monitor.best_tip = block_processor.get_best_block_hash()
zmq_thread = Thread(target=chain_monitor.monitor_chain_zmq, daemon=True) zmq_thread = Thread(target=chain_monitor.monitor_chain_zmq, daemon=True)
zmq_thread.start() zmq_thread.start()
@@ -106,9 +106,9 @@ def test_monitor_chain_zmq(db_manager):
assert chain_monitor.responder_queue.empty() 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 # 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.best_tip = None
chain_monitor.monitor_chain() chain_monitor.monitor_chain()
@@ -131,15 +131,16 @@ def test_monitor_chain(db_manager):
generate_block() 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 # 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.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 # 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. # been added once.
chain_monitor.monitor_chain(polling_delta=2) chain_monitor.monitor_chain()
generate_block() generate_block()
watcher_block = chain_monitor.watcher_queue.get() watcher_block = chain_monitor.watcher_queue.get()

View File

@@ -1,26 +1,27 @@
from binascii import unhexlify from binascii import unhexlify
from teos.errors import * 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 import LOG_PREFIX
from teos.inspector import Inspector
from teos.block_processor import BlockProcessor
import common.cryptographer 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) common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX)
inspector = Inspector(get_config())
APPOINTMENT_OK = (0, None) APPOINTMENT_OK = (0, None)
NO_HEX_STRINGS = [ NO_HEX_STRINGS = [
"R" * LOCATOR_LEN_HEX, "R" * LOCATOR_LEN_HEX,
get_random_value_hex(LOCATOR_LEN_BYTES - 1) + "PP", 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()] 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(): def test_check_locator():
# Right appointment type, size and format # Right appointment type, size and format
@@ -200,7 +206,7 @@ def test_inspect(run_bitcoind):
# Valid appointment # Valid appointment
locator = get_random_value_hex(LOCATOR_LEN_BYTES) 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 end_time = start_time + 20
to_self_delay = MIN_TO_SELF_DELAY to_self_delay = MIN_TO_SELF_DELAY
encrypted_blob = get_random_value_hex(64) encrypted_blob = get_random_value_hex(64)

View File

@@ -1,27 +1,33 @@
import json import json
import pytest import pytest
import random import random
from queue import Queue
from uuid import uuid4 from uuid import uuid4
from queue import Queue
from shutil import rmtree from shutil import rmtree
from copy import deepcopy from copy import deepcopy
from threading import Thread from threading import Thread
from teos.db_manager import DBManager from teos.carrier import Carrier
from teos.responder import Responder, TransactionTracker
from teos.block_processor import BlockProcessor
from teos.chain_monitor import ChainMonitor
from teos.tools import bitcoin_cli 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 common.constants import LOCATOR_LEN_HEX
from bitcoind_mock.transaction import create_dummy_transaction, create_tx_from_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") @pytest.fixture(scope="module")
def responder(db_manager): def responder(db_manager, carrier, block_processor):
responder = Responder(db_manager) responder = Responder(db_manager, carrier, block_processor)
chain_monitor = ChainMonitor(Queue(), responder.block_queue) chain_monitor = ChainMonitor(Queue(), responder.block_queue, block_processor, bitcoind_feed_params)
chain_monitor.monitor_chain() chain_monitor.monitor_chain()
return responder return responder
@@ -61,7 +67,7 @@ def create_dummy_tracker_data(random_txid=False, penalty_rawtx=None):
if random_txid is True: if random_txid is True:
penalty_txid = get_random_value_hex(32) 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] locator = dispute_txid[:LOCATOR_LEN_HEX]
return locator, dispute_txid, penalty_txid, penalty_rawtx, appointment_end 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 # We're on sync if we're 1 or less blocks behind the tip
chain_tip = BlockProcessor.get_best_block_hash() chain_tip = block_processor.get_best_block_hash()
assert Responder.on_sync(chain_tip) is True assert responder.on_sync(chain_tip) is True
generate_block() 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 # 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) generate_blocks(2)
assert Responder.on_sync(chain_tip) is False assert responder.on_sync(chain_tip) is False
def test_tracker_to_dict(): def test_tracker_to_dict():
@@ -147,8 +153,8 @@ def test_tracker_from_dict_invalid_data():
assert True assert True
def test_init_responder(temp_db_manager): def test_init_responder(temp_db_manager, carrier, block_processor):
responder = Responder(temp_db_manager) responder = Responder(temp_db_manager, carrier, block_processor)
assert isinstance(responder.trackers, dict) and len(responder.trackers) == 0 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.tx_tracker_map, dict) and len(responder.tx_tracker_map) == 0
assert isinstance(responder.unconfirmed_txs, list) and len(responder.unconfirmed_txs) == 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() assert responder.block_queue.empty()
def test_handle_breach(db_manager): def test_handle_breach(db_manager, carrier, block_processor):
responder = Responder(db_manager) responder = Responder(db_manager, carrier, block_processor)
uuid = uuid4().hex uuid = uuid4().hex
tracker = create_dummy_tracker() tracker = create_dummy_tracker()
@@ -176,7 +182,11 @@ def test_handle_breach(db_manager):
assert receipt.delivered is True 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 uuid = uuid4().hex
tracker = create_dummy_tracker() tracker = create_dummy_tracker()
@@ -262,10 +272,10 @@ def test_add_tracker_already_confirmed(responder):
assert penalty_txid not in responder.unconfirmed_txs 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 # Create a fresh responder to simplify the test
responder = Responder(temp_db_manager) responder = Responder(temp_db_manager, carrier, block_processor)
chain_monitor = ChainMonitor(Queue(), responder.block_queue) chain_monitor = ChainMonitor(Queue(), responder.block_queue, block_processor, bitcoind_feed_params)
chain_monitor.monitor_chain() chain_monitor.monitor_chain()
trackers = [create_dummy_tracker(penalty_rawtx=create_dummy_transaction().hex()) for _ in range(20)] 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 # And broadcast some of the transactions
broadcast_txs = [] broadcast_txs = []
for tracker in trackers[:5]: 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) broadcast_txs.append(tracker.penalty_txid)
# Mine a block # Mine a block
@@ -312,7 +322,7 @@ def test_do_watch(temp_db_manager):
# Do the rest # Do the rest
broadcast_txs = [] broadcast_txs = []
for tracker in trackers[5:]: 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) broadcast_txs.append(tracker.penalty_txid)
# Mine a block # Mine a block
@@ -321,9 +331,9 @@ def test_do_watch(temp_db_manager):
assert len(responder.tx_tracker_map) == 0 assert len(responder.tx_tracker_map) == 0
def test_check_confirmations(db_manager): def test_check_confirmations(db_manager, carrier, block_processor):
responder = Responder(db_manager) responder = Responder(db_manager, carrier, block_processor)
chain_monitor = ChainMonitor(Queue(), responder.block_queue) chain_monitor = ChainMonitor(Queue(), responder.block_queue, block_processor, bitcoind_feed_params)
chain_monitor.monitor_chain() chain_monitor.monitor_chain()
# check_confirmations checks, given a list of transaction for a block, what of the known penalty transaction have # 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()) assert txs_to_rebroadcast == list(txs_missing_too_many_conf.keys())
def test_get_completed_trackers(db_manager): def test_get_completed_trackers(db_manager, carrier, block_processor):
initial_height = bitcoin_cli().getblockcount() initial_height = bitcoin_cli(bitcoind_connect_params).getblockcount()
responder = Responder(db_manager) responder = Responder(db_manager, carrier, block_processor)
chain_monitor = ChainMonitor(Queue(), responder.block_queue) chain_monitor = ChainMonitor(Queue(), responder.block_queue, block_processor, bitcoind_feed_params)
chain_monitor.monitor_chain() chain_monitor.monitor_chain()
# A complete tracker is a tracker that has reached the appointment end with enough confs (> MIN_CONFIRMATIONS) # 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(): 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 # The dummy appointments have a end_appointment time of current + 2, but trackers need at least 6 confs by default
generate_blocks(6) generate_blocks(6)
@@ -438,9 +448,9 @@ def test_get_completed_trackers(db_manager):
assert set(completed_trackers_ids) == set(ended_trackers_keys) assert set(completed_trackers_ids) == set(ended_trackers_keys)
def test_rebroadcast(db_manager): def test_rebroadcast(db_manager, carrier, block_processor):
responder = Responder(db_manager) responder = Responder(db_manager, carrier, block_processor)
chain_monitor = ChainMonitor(Queue(), responder.block_queue) chain_monitor = ChainMonitor(Queue(), responder.block_queue, block_processor, bitcoind_feed_params)
chain_monitor.monitor_chain() chain_monitor.monitor_chain()
txs_to_rebroadcast = [] txs_to_rebroadcast = []

View File

@@ -1,17 +1,17 @@
from teos.tools import can_connect_to_bitcoind, in_correct_network, bitcoin_cli from teos.tools import can_connect_to_bitcoind, in_correct_network, bitcoin_cli
from common.tools import check_sha256_hex_format from common.tools import check_sha256_hex_format
from test.teos.unit.conftest import bitcoind_connect_params
def test_in_correct_network(run_bitcoind): def test_in_correct_network(run_bitcoind):
# The simulator runs as if it was regtest, so every other network should fail # 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(bitcoind_connect_params, "mainnet") is False
assert in_correct_network("testnet") is False assert in_correct_network(bitcoind_connect_params, "testnet") is False
assert in_correct_network("regtest") is True assert in_correct_network(bitcoind_connect_params, "regtest") is True
def test_can_connect_to_bitcoind(): 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(): # def test_can_connect_to_bitcoind_bitcoin_not_running():
@@ -22,7 +22,7 @@ def test_can_connect_to_bitcoind():
def test_bitcoin_cli(): def test_bitcoin_cli():
try: try:
bitcoin_cli().help() bitcoin_cli(bitcoind_connect_params).help()
assert True assert True
except Exception: except Exception:

View File

@@ -4,11 +4,19 @@ from shutil import rmtree
from threading import Thread from threading import Thread
from coincurve import PrivateKey from coincurve import PrivateKey
from teos import LOG_PREFIX
from teos.carrier import Carrier
from teos.watcher import Watcher from teos.watcher import Watcher
from teos.responder import Responder
from teos.tools import bitcoin_cli 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.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 ( from test.teos.unit.conftest import (
generate_blocks, generate_blocks,
@@ -16,14 +24,9 @@ from test.teos.unit.conftest import (
get_random_value_hex, get_random_value_hex,
generate_keypair, generate_keypair,
get_config, 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) common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX)
@@ -33,6 +36,7 @@ START_TIME_OFFSET = 1
END_TIME_OFFSET = 1 END_TIME_OFFSET = 1
TEST_SET_SIZE = 200 TEST_SET_SIZE = 200
config = get_config()
signing_key, public_key = generate_keypair() signing_key, public_key = generate_keypair()
@@ -50,8 +54,22 @@ def temp_db_manager():
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def watcher(db_manager): def watcher(db_manager):
watcher = Watcher(db_manager, Responder(db_manager), signing_key.to_der(), get_config()) block_processor = BlockProcessor(bitcoind_connect_params)
chain_monitor = ChainMonitor(watcher.block_queue, watcher.responder.block_queue) 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() chain_monitor.monitor_chain()
return watcher 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.appointments, dict) and len(watcher.appointments) == 0
assert isinstance(watcher.locator_uuid_map, dict) and len(watcher.locator_uuid_map) == 0 assert isinstance(watcher.locator_uuid_map, dict) and len(watcher.locator_uuid_map) == 0
assert watcher.block_queue.empty() assert watcher.block_queue.empty()
assert isinstance(watcher.config, dict) assert isinstance(watcher.block_processor, BlockProcessor)
assert isinstance(watcher.signing_key, PrivateKey)
assert isinstance(watcher.responder, Responder) 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): def test_add_appointment(watcher):
@@ -120,7 +140,7 @@ def test_add_too_many_appointments(watcher):
# Any appointment on top of those should fail # Any appointment on top of those should fail
watcher.appointments = dict() watcher.appointments = dict()
for _ in range(MAX_APPOINTMENTS): for _ in range(config.get("MAX_APPOINTMENTS")):
appointment, dispute_tx = generate_dummy_appointment( appointment, dispute_tx = generate_dummy_appointment(
start_time_offset=START_TIME_OFFSET, end_time_offset=END_TIME_OFFSET 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 # Broadcast the first two
for dispute_tx in dispute_txs[:2]: 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 # After generating enough blocks, the number of appointments should have reduced by two
generate_blocks(START_TIME_OFFSET + END_TIME_OFFSET) 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 # The rest of appointments will timeout after the end (2) + EXPIRY_DELTA
# Wait for an additional block to be safe # 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 assert len(watcher.appointments) == 0