diff --git a/hathor/builder/builder.py b/hathor/builder/builder.py index a788b72ed..de8410bb2 100644 --- a/hathor/builder/builder.py +++ b/hathor/builder/builder.py @@ -19,7 +19,6 @@ from typing_extensions import assert_never from hathor.checkpoint import Checkpoint -from hathor.conf.get_settings import get_global_settings from hathor.conf.settings import HathorSettings as HathorSettingsType from hathor.consensus import ConsensusAlgorithm from hathor.consensus.poa import PoaBlockProducer, PoaSigner @@ -346,7 +345,7 @@ def set_peer_id(self, peer_id: PeerId) -> 'Builder': def _get_or_create_settings(self) -> HathorSettingsType: """Return the HathorSettings instance set on this builder, or a new one if not set.""" if self._settings is None: - self._settings = get_global_settings() + raise ValueError('settings not set') return self._settings def _get_reactor(self) -> Reactor: @@ -422,7 +421,8 @@ def _get_or_create_p2p_manager(self) -> ConnectionsManager: assert self._network is not None self._p2p_manager = ConnectionsManager( - reactor, + settings=self._get_or_create_settings(), + reactor=reactor, network=self._network, my_peer=my_peer, pubsub=self._get_or_create_pubsub(), diff --git a/hathor/builder/cli_builder.py b/hathor/builder/cli_builder.py index d570efe9f..9c41d73fb 100644 --- a/hathor/builder/cli_builder.py +++ b/hathor/builder/cli_builder.py @@ -295,7 +295,8 @@ def create_manager(self, reactor: Reactor) -> HathorManager: cpu_mining_service = CpuMiningService() p2p_manager = ConnectionsManager( - reactor, + settings=settings, + reactor=reactor, network=network, my_peer=peer_id, pubsub=pubsub, diff --git a/hathor/consensus/consensus_settings.py b/hathor/consensus/consensus_settings.py index 01c123230..259b35f99 100644 --- a/hathor/consensus/consensus_settings.py +++ b/hathor/consensus/consensus_settings.py @@ -12,14 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +import hashlib from abc import ABC, abstractmethod from enum import Enum, unique from typing import Annotated, Any, Literal, TypeAlias -from pydantic import Field, NonNegativeInt, validator +from pydantic import Field, NonNegativeInt, PrivateAttr, validator from typing_extensions import override from hathor.transaction import TxVersion +from hathor.util import json_dumpb from hathor.utils.pydantic import BaseModel @@ -31,6 +33,7 @@ class ConsensusType(str, Enum): class _BaseConsensusSettings(ABC, BaseModel): type: ConsensusType + _peer_hello_hash: str | None = PrivateAttr(default=None) def is_pow(self) -> bool: """Return whether this is a Proof-of-Work consensus.""" @@ -49,6 +52,16 @@ def is_vertex_version_valid(self, version: TxVersion, include_genesis: bool = Fa """Return whether a `TxVersion` is valid for this consensus type.""" return version in self._get_valid_vertex_versions(include_genesis) + def get_peer_hello_hash(self) -> str | None: + """Return a hash of consensus settings to be used in peer hello validation.""" + if self._peer_hello_hash is None: + self._peer_hello_hash = self._calculate_peer_hello_hash() + return self._peer_hello_hash + + def _calculate_peer_hello_hash(self) -> str | None: + """Calculate a hash of consensus settings to be used in peer hello validation.""" + return None + class PowSettings(_BaseConsensusSettings): type: Literal[ConsensusType.PROOF_OF_WORK] = ConsensusType.PROOF_OF_WORK @@ -62,6 +75,10 @@ def _get_valid_vertex_versions(self, include_genesis: bool) -> set[TxVersion]: TxVersion.MERGE_MINED_BLOCK } + @override + def get_peer_hello_hash(self) -> str | None: + return None + class PoaSignerSettings(BaseModel): public_key: bytes @@ -86,6 +103,13 @@ def _validate_end_height(cls, end_height: int | None, values: dict[str, Any]) -> return end_height + def to_json_dict(self) -> dict[str, Any]: + """Return this signer settings instance as a json dict.""" + json_dict = self.dict() + # TODO: We can use a custom serializer to convert bytes to hex when we update to Pydantic V2. + json_dict['public_key'] = self.public_key.hex() + return json_dict + class PoaSettings(_BaseConsensusSettings): type: Literal[ConsensusType.PROOF_OF_AUTHORITY] = ConsensusType.PROOF_OF_AUTHORITY @@ -114,5 +138,12 @@ def _get_valid_vertex_versions(self, include_genesis: bool) -> set[TxVersion]: return versions + @override + def _calculate_peer_hello_hash(self) -> str | None: + data = b'' + for signer in self.signers: + data += json_dumpb(signer.to_json_dict()) + return hashlib.sha256(data).digest().hex() + ConsensusSettings: TypeAlias = Annotated[PowSettings | PoaSettings, Field(discriminator='type')] diff --git a/hathor/consensus/poa/poa.py b/hathor/consensus/poa/poa.py index e214b331a..57ef4fd2c 100644 --- a/hathor/consensus/poa/poa.py +++ b/hathor/consensus/poa/poa.py @@ -82,10 +82,9 @@ def verify_poa_signature(settings: PoaSettings, block: PoaBlock) -> InvalidSigna """Return whether the provided public key was used to sign the block Proof-of-Authority.""" from hathor.consensus.poa import PoaSigner active_signers = get_active_signers(settings, block.get_height()) - sorted_signers = sorted(active_signers) hashed_poa_data = get_hashed_poa_data(block) - for signer_index, public_key_bytes in enumerate(sorted_signers): + for signer_index, public_key_bytes in enumerate(active_signers): signer_id = PoaSigner.get_poa_signer_id(public_key_bytes) if block.signer_id != signer_id: # this is not our signer diff --git a/hathor/consensus/poa/poa_block_producer.py b/hathor/consensus/poa/poa_block_producer.py index 9d7b37f71..ca7a1a151 100644 --- a/hathor/consensus/poa/poa_block_producer.py +++ b/hathor/consensus/poa/poa_block_producer.py @@ -108,9 +108,8 @@ def _get_signer_index(self, previous_block: Block) -> int | None: public_key = self._poa_signer.get_public_key() public_key_bytes = get_public_key_bytes_compressed(public_key) active_signers = poa.get_active_signers(self._poa_settings, height) - sorted_signers = sorted(active_signers) try: - return sorted_signers.index(public_key_bytes) + return active_signers.index(public_key_bytes) except ValueError: return None diff --git a/hathor/p2p/factory.py b/hathor/p2p/factory.py index da02328d3..a90cf2882 100644 --- a/hathor/p2p/factory.py +++ b/hathor/p2p/factory.py @@ -17,6 +17,7 @@ from twisted.internet import protocol from twisted.internet.interfaces import IAddress +from hathor.conf.settings import HathorSettings from hathor.p2p.manager import ConnectionsManager from hathor.p2p.peer_id import PeerId from hathor.p2p.protocol import HathorLineReceiver @@ -36,14 +37,16 @@ class HathorServerFactory(protocol.ServerFactory): protocol: type[MyServerProtocol] = MyServerProtocol def __init__( - self, - network: str, - my_peer: PeerId, - p2p_manager: ConnectionsManager, - *, - use_ssl: bool, + self, + network: str, + my_peer: PeerId, + p2p_manager: ConnectionsManager, + *, + settings: HathorSettings, + use_ssl: bool, ): super().__init__() + self._settings = settings self.network = network self.my_peer = my_peer self.p2p_manager = p2p_manager @@ -57,6 +60,7 @@ def buildProtocol(self, addr: IAddress) -> MyServerProtocol: p2p_manager=self.p2p_manager, use_ssl=self.use_ssl, inbound=True, + settings=self._settings ) p.factory = self return p @@ -69,14 +73,16 @@ class HathorClientFactory(protocol.ClientFactory): protocol: type[MyClientProtocol] = MyClientProtocol def __init__( - self, - network: str, - my_peer: PeerId, - p2p_manager: ConnectionsManager, - *, - use_ssl: bool, + self, + network: str, + my_peer: PeerId, + p2p_manager: ConnectionsManager, + *, + settings: HathorSettings, + use_ssl: bool, ): super().__init__() + self._settings = settings self.network = network self.my_peer = my_peer self.p2p_manager = p2p_manager @@ -90,6 +96,7 @@ def buildProtocol(self, addr: IAddress) -> MyClientProtocol: p2p_manager=self.p2p_manager, use_ssl=self.use_ssl, inbound=False, + settings=self._settings ) p.factory = self return p diff --git a/hathor/p2p/manager.py b/hathor/p2p/manager.py index 45b3e92f5..b34254282 100644 --- a/hathor/p2p/manager.py +++ b/hathor/p2p/manager.py @@ -24,7 +24,7 @@ from twisted.python.failure import Failure from twisted.web.client import Agent -from hathor.conf.get_settings import get_global_settings +from hathor.conf.settings import HathorSettings from hathor.p2p.entrypoint import Entrypoint from hathor.p2p.netfilter.factory import NetfilterFactory from hathor.p2p.peer_discovery import PeerDiscovery @@ -45,7 +45,6 @@ from hathor.manager import HathorManager logger = get_logger() -settings = get_global_settings() # The timeout in seconds for the whitelist GET request WHITELIST_REQUEST_TIMEOUT = 45 @@ -74,9 +73,6 @@ class PeerConnectionsMetrics(NamedTuple): class ConnectionsManager: """ It manages all peer-to-peer connections and events related to control messages. """ - MAX_ENABLED_SYNC = settings.MAX_ENABLED_SYNC - SYNC_UPDATE_INTERVAL = settings.SYNC_UPDATE_INTERVAL - PEER_DISCOVERY_INTERVAL = settings.PEER_DISCOVERY_INTERVAL class GlobalRateLimiter: SEND_TIPS = 'NodeSyncTimestamp.send_tips' @@ -92,18 +88,25 @@ class GlobalRateLimiter: rate_limiter: RateLimiter - def __init__(self, - reactor: Reactor, - network: str, - my_peer: PeerId, - pubsub: PubSubManager, - ssl: bool, - rng: Random, - whitelist_only: bool) -> None: + def __init__( + self, + settings: HathorSettings, + reactor: Reactor, + network: str, + my_peer: PeerId, + pubsub: PubSubManager, + ssl: bool, + rng: Random, + whitelist_only: bool, + ) -> None: self.log = logger.new() + self._settings = settings self.rng = rng self.manager = None - self._settings = get_global_settings() + + self.MAX_ENABLED_SYNC = settings.MAX_ENABLED_SYNC + self.SYNC_UPDATE_INTERVAL = settings.SYNC_UPDATE_INTERVAL + self.PEER_DISCOVERY_INTERVAL = settings.PEER_DISCOVERY_INTERVAL self.reactor = reactor self.my_peer = my_peer @@ -125,8 +128,12 @@ def __init__(self, # Factories. from hathor.p2p.factory import HathorClientFactory, HathorServerFactory self.use_ssl = ssl - self.server_factory = HathorServerFactory(self.network, self.my_peer, p2p_manager=self, use_ssl=self.use_ssl) - self.client_factory = HathorClientFactory(self.network, self.my_peer, p2p_manager=self, use_ssl=self.use_ssl) + self.server_factory = HathorServerFactory( + self.network, self.my_peer, p2p_manager=self, use_ssl=self.use_ssl, settings=self._settings + ) + self.client_factory = HathorClientFactory( + self.network, self.my_peer, p2p_manager=self, use_ssl=self.use_ssl, settings=self._settings + ) # Global maximum number of connections. self.max_connections: int = self._settings.PEER_MAX_CONNECTIONS diff --git a/hathor/p2p/protocol.py b/hathor/p2p/protocol.py index dfaf1a46b..99c63b29e 100644 --- a/hathor/p2p/protocol.py +++ b/hathor/p2p/protocol.py @@ -23,7 +23,7 @@ from twisted.protocols.basic import LineReceiver from twisted.python.failure import Failure -from hathor.conf.get_settings import get_global_settings +from hathor.conf.settings import HathorSettings from hathor.p2p.entrypoint import Entrypoint from hathor.p2p.messages import ProtocolMessages from hathor.p2p.peer_id import PeerId @@ -91,9 +91,17 @@ class WarningFlags(str, Enum): sync_version: Optional[SyncVersion] # version chosen to be used on this connection capabilities: set[str] # capabilities received from the peer in HelloState - def __init__(self, network: str, my_peer: PeerId, p2p_manager: 'ConnectionsManager', - *, use_ssl: bool, inbound: bool) -> None: - self._settings = get_global_settings() + def __init__( + self, + network: str, + my_peer: PeerId, + p2p_manager: 'ConnectionsManager', + *, + settings: HathorSettings, + use_ssl: bool, + inbound: bool, + ) -> None: + self._settings = settings self.network = network self.my_peer = my_peer self.connections = p2p_manager @@ -164,7 +172,7 @@ def change_state(self, state_enum: PeerState) -> None: """Called to change the state of the connection.""" if state_enum not in self._state_instances: state_cls = state_enum.value - instance = state_cls(self) + instance = state_cls(self, self._settings) instance.state_name = state_enum.name self._state_instances[state_enum] = instance new_state = self._state_instances[state_enum] diff --git a/hathor/p2p/states/base.py b/hathor/p2p/states/base.py index abbc17dd0..f08401cc0 100644 --- a/hathor/p2p/states/base.py +++ b/hathor/p2p/states/base.py @@ -18,6 +18,7 @@ from structlog import get_logger from twisted.internet.defer import Deferred +from hathor.conf.settings import HathorSettings from hathor.p2p.messages import ProtocolMessages if TYPE_CHECKING: @@ -33,8 +34,9 @@ class BaseState: Callable[[str], None] | Callable[[str], Deferred[None]] | Callable[[str], Coroutine[Deferred[None], Any, None]] ] - def __init__(self, protocol: 'HathorProtocol'): + def __init__(self, protocol: 'HathorProtocol', settings: HathorSettings): self.log = logger.new(**protocol.get_logger_context()) + self._settings = settings self.protocol = protocol self.cmd_map = { ProtocolMessages.ERROR: self.handle_error, diff --git a/hathor/p2p/states/hello.py b/hathor/p2p/states/hello.py index c17cc2be4..b7cb42dce 100644 --- a/hathor/p2p/states/hello.py +++ b/hathor/p2p/states/hello.py @@ -18,6 +18,7 @@ import hathor from hathor.conf.get_settings import get_global_settings +from hathor.conf.settings import HathorSettings from hathor.exception import HathorError from hathor.p2p.messages import ProtocolMessages from hathor.p2p.states.base import BaseState @@ -32,9 +33,8 @@ class HelloState(BaseState): - def __init__(self, protocol: 'HathorProtocol') -> None: - super().__init__(protocol) - self._settings = get_global_settings() + def __init__(self, protocol: 'HathorProtocol', settings: HathorSettings) -> None: + super().__init__(protocol, settings) self.log = logger.new(**protocol.get_logger_context()) self.cmd_map.update({ ProtocolMessages.HELLO: self.handle_hello, @@ -56,7 +56,7 @@ def _get_hello_data(self) -> dict[str, Any]: 'remote_address': format_address(remote), 'genesis_short_hash': get_genesis_short_hash(), 'timestamp': protocol.node.reactor.seconds(), - 'settings_dict': get_settings_hello_dict(), + 'settings_dict': get_settings_hello_dict(self._settings), 'capabilities': protocol.node.capabilities, } if self.protocol.node.has_sync_version_capability(): @@ -150,7 +150,7 @@ def handle_hello(self, payload: str) -> None: if 'settings_dict' in data: # If settings_dict is sent we must validate it - settings_dict = get_settings_hello_dict() + settings_dict = get_settings_hello_dict(self._settings) if data['settings_dict'] != settings_dict: protocol.send_error_and_close_connection( 'Settings values are different. {}'.format(json_dumps(settings_dict)) diff --git a/hathor/p2p/states/peer_id.py b/hathor/p2p/states/peer_id.py index b2e1f0a50..525303adf 100644 --- a/hathor/p2p/states/peer_id.py +++ b/hathor/p2p/states/peer_id.py @@ -16,7 +16,7 @@ from structlog import get_logger -from hathor.conf import HathorSettings +from hathor.conf.settings import HathorSettings from hathor.p2p.messages import ProtocolMessages from hathor.p2p.peer_id import PeerId from hathor.p2p.states.base import BaseState @@ -27,12 +27,10 @@ logger = get_logger() -settings = HathorSettings() - class PeerIdState(BaseState): - def __init__(self, protocol: 'HathorProtocol') -> None: - super().__init__(protocol) + def __init__(self, protocol: 'HathorProtocol', settings: HathorSettings) -> None: + super().__init__(protocol, settings) self.log = logger.new(remote=protocol.get_short_remote()) self.cmd_map.update({ ProtocolMessages.PEER_ID: self.handle_peer_id, @@ -100,7 +98,7 @@ async def handle_peer_id(self, payload: str) -> None: # is it on the whitelist? if peer.id and self._should_block_peer(peer.id): - if settings.WHITELIST_WARN_BLOCKED_PEERS: + if self._settings.WHITELIST_WARN_BLOCKED_PEERS: protocol.send_error_and_close_connection(f'Blocked (by {peer.id}). Get in touch with Hathor team.') else: protocol.send_error_and_close_connection('Connection rejected.') @@ -152,12 +150,12 @@ def _should_block_peer(self, peer_id: str) -> bool: return False # when ENABLE_PEER_WHITELIST is set, we check if we're on sync-v1 to block non-whitelisted peers - if settings.ENABLE_PEER_WHITELIST: + if self._settings.ENABLE_PEER_WHITELIST: assert self.protocol.sync_version is not None if not peer_is_whitelisted: if self.protocol.sync_version.is_v1(): return True - elif settings.USE_PEER_WHITELIST_ON_SYNC_V2: + elif self._settings.USE_PEER_WHITELIST_ON_SYNC_V2: return True # otherwise we block non-whitelisted peers when on "whitelist-only mode" diff --git a/hathor/p2p/states/ready.py b/hathor/p2p/states/ready.py index ec20975f6..57a01c6d4 100644 --- a/hathor/p2p/states/ready.py +++ b/hathor/p2p/states/ready.py @@ -18,7 +18,7 @@ from structlog import get_logger from twisted.internet.task import LoopingCall -from hathor.conf.get_settings import get_global_settings +from hathor.conf.settings import HathorSettings from hathor.indexes.height_index import HeightInfo from hathor.p2p.messages import ProtocolMessages from hathor.p2p.peer_id import PeerId @@ -35,9 +35,8 @@ class ReadyState(BaseState): - def __init__(self, protocol: 'HathorProtocol') -> None: - super().__init__(protocol) - self._settings = get_global_settings() + def __init__(self, protocol: 'HathorProtocol', settings: HathorSettings) -> None: + super().__init__(protocol, settings) self.log = logger.new(**self.protocol.get_logger_context()) diff --git a/hathor/p2p/utils.py b/hathor/p2p/utils.py index 5683224f9..0dfe9ebc1 100644 --- a/hathor/p2p/utils.py +++ b/hathor/p2p/utils.py @@ -27,6 +27,7 @@ from twisted.internet.interfaces import IAddress from hathor.conf.get_settings import get_global_settings +from hathor.conf.settings import HathorSettings from hathor.indexes.height_index import HeightInfo from hathor.p2p.entrypoint import Entrypoint from hathor.p2p.peer_discovery import DNSPeerDiscovery @@ -59,10 +60,9 @@ def get_genesis_short_hash() -> str: return get_representation_for_all_genesis(settings).hex()[:7] -def get_settings_hello_dict() -> dict[str, Any]: +def get_settings_hello_dict(settings: HathorSettings) -> dict[str, Any]: """ Return a dict of settings values that must be validated in the hello state """ - settings = get_global_settings() settings_dict = {} for key in settings.P2P_SETTINGS_HASH_FIELDS: value = getattr(settings, key) @@ -70,6 +70,10 @@ def get_settings_hello_dict() -> dict[str, Any]: if type(value) is bytes: value = value.hex() settings_dict[key] = value + + if consensus_hash := settings.CONSENSUS_ALGORITHM.get_peer_hello_hash(): + settings_dict['CONSENSUS_ALGORITHM'] = consensus_hash + return settings_dict diff --git a/tests/others/test_metrics.py b/tests/others/test_metrics.py index ded64ad6c..2d21d0c57 100644 --- a/tests/others/test_metrics.py +++ b/tests/others/test_metrics.py @@ -218,7 +218,8 @@ def build_hathor_protocol(): my_peer=my_peer, p2p_manager=manager.connections, use_ssl=False, - inbound=False + inbound=False, + settings=self._settings ) protocol.peer = PeerId() diff --git a/tests/p2p/test_bootstrap.py b/tests/p2p/test_bootstrap.py index 5a691086a..b6a851a5c 100644 --- a/tests/p2p/test_bootstrap.py +++ b/tests/p2p/test_bootstrap.py @@ -48,7 +48,7 @@ def do_lookup_text(self, host: str) -> Deferred[LookupResult]: class BootstrapTestCase(unittest.TestCase): def test_mock_discovery(self) -> None: pubsub = PubSubManager(self.clock) - connections = ConnectionsManager(self.clock, 'testnet', PeerId(), pubsub, True, self.rng, True) + connections = ConnectionsManager(self._settings, self.clock, 'testnet', PeerId(), pubsub, True, self.rng, True) host_ports1 = [ ('foobar', 1234), ('127.0.0.99', 9999), @@ -71,7 +71,7 @@ def test_mock_discovery(self) -> None: def test_dns_discovery(self) -> None: pubsub = PubSubManager(self.clock) - connections = ConnectionsManager(self.clock, 'testnet', PeerId(), pubsub, True, self.rng, True) + connections = ConnectionsManager(self._settings, self.clock, 'testnet', PeerId(), pubsub, True, self.rng, True) bootstrap_a = [ '127.0.0.99', '127.0.0.88', diff --git a/tests/p2p/test_whitelist.py b/tests/p2p/test_whitelist.py index 5cbc7e4ae..db854ff63 100644 --- a/tests/p2p/test_whitelist.py +++ b/tests/p2p/test_whitelist.py @@ -12,13 +12,11 @@ from hathor.simulator import FakeConnection from tests import unittest -settings = get_global_settings() - class WhitelistTestCase(unittest.SyncV1Params, unittest.TestCase): - @patch('hathor.p2p.states.peer_id.settings', new=settings._replace(ENABLE_PEER_WHITELIST=True)) def test_sync_v11_whitelist_no_no(self) -> None: network = 'testnet' + self._settings = get_global_settings()._replace(ENABLE_PEER_WHITELIST=True) manager1 = self.create_peer(network) self.assertEqual(manager1.connections.get_enabled_sync_versions(), {SyncVersion.V1_1}) @@ -38,9 +36,9 @@ def test_sync_v11_whitelist_no_no(self) -> None: self.assertTrue(conn.tr1.disconnecting) self.assertTrue(conn.tr2.disconnecting) - @patch('hathor.p2p.states.peer_id.settings', new=settings._replace(ENABLE_PEER_WHITELIST=True)) def test_sync_v11_whitelist_yes_no(self) -> None: network = 'testnet' + self._settings = get_global_settings()._replace(ENABLE_PEER_WHITELIST=True) manager1 = self.create_peer(network) self.assertEqual(manager1.connections.get_enabled_sync_versions(), {SyncVersion.V1_1}) @@ -62,9 +60,9 @@ def test_sync_v11_whitelist_yes_no(self) -> None: self.assertFalse(conn.tr1.disconnecting) self.assertTrue(conn.tr2.disconnecting) - @patch('hathor.p2p.states.peer_id.settings', new=settings._replace(ENABLE_PEER_WHITELIST=True)) def test_sync_v11_whitelist_yes_yes(self) -> None: network = 'testnet' + self._settings = get_global_settings()._replace(ENABLE_PEER_WHITELIST=True) manager1 = self.create_peer(network) self.assertEqual(manager1.connections.get_enabled_sync_versions(), {SyncVersion.V1_1}) diff --git a/tests/poa/test_poa.py b/tests/poa/test_poa.py index 89294b009..3026701ff 100644 --- a/tests/poa/test_poa.py +++ b/tests/poa/test_poa.py @@ -162,11 +162,10 @@ def get_signer() -> tuple[PoaSigner, bytes]: # For this part we use two signers, so the ordering matters signer_and_keys: list[tuple[PoaSigner, bytes]] = [get_signer(), get_signer()] - sorted_keys = sorted(signer_and_keys, key=lambda key_pair: key_pair[1]) # sort by public key settings.CONSENSUS_ALGORITHM = PoaSettings(signers=tuple( [PoaSignerSettings(public_key=key_pair[1]) for key_pair in signer_and_keys] )) - first_poa_signer, second_poa_signer = [key_pair[0] for key_pair in sorted_keys] + first_poa_signer, second_poa_signer = [key_pair[0] for key_pair in signer_and_keys] # Test valid signature with two signers, in turn block.weight = poa.BLOCK_WEIGHT_IN_TURN diff --git a/tests/poa/test_poa_block_producer.py b/tests/poa/test_poa_block_producer.py index 005b3b5f6..ac71b1ba9 100644 --- a/tests/poa/test_poa_block_producer.py +++ b/tests/poa/test_poa_block_producer.py @@ -100,8 +100,7 @@ def test_poa_block_producer_one_signer() -> None: def test_poa_block_producer_two_signers() -> None: - signers = [get_signer(), get_signer()] - signer1, signer2 = sorted(signers, key=lambda signer: get_public_key_bytes_compressed(signer.get_public_key())) + signer1, signer2 = get_signer(), get_signer() settings = get_settings(signer1, signer2, time_between_blocks=10) manager = _get_manager(settings) reactor = manager.reactor @@ -175,10 +174,10 @@ def test_poa_block_producer_two_signers() -> None: ) def test_expected_block_timestamp(previous_height: int, signer_index: int, expected_delay: int) -> None: signers = [get_signer(), get_signer(), get_signer(), get_signer()] - keys_and_signers = sorted( + keys_and_signers = [ (get_public_key_bytes_compressed(signer.get_public_key()), signer) for signer in signers - ) + ] signer = keys_and_signers[signer_index][1] settings = Mock() settings.CONSENSUS_ALGORITHM = PoaSettings(signers=tuple( diff --git a/tests/poa/test_poa_simulation.py b/tests/poa/test_poa_simulation.py index cb2bd9475..eb5907162 100644 --- a/tests/poa/test_poa_simulation.py +++ b/tests/poa/test_poa_simulation.py @@ -95,6 +95,20 @@ def test_no_producers(self) -> None: assert manager1.tx_storage.get_block_count() == 1 assert manager2.tx_storage.get_block_count() == 1 + def test_different_signer_settings(self) -> None: + signer1, signer2 = get_signer(), get_signer() + self.simulator.settings = get_settings(signer1) + manager1 = self._get_manager() + self.simulator.settings = get_settings(signer2) + manager2 = self._get_manager() + + connection = FakeConnection(manager1, manager2) + self.simulator.add_connection(connection) + + connection.run_one_step() + assert b'ERROR Settings values are different' in connection.peek_tr1_value() + assert connection.tr1.disconnecting + def test_one_producer_allowed(self) -> None: signer = get_signer() signer_id = signer._signer_id @@ -131,8 +145,7 @@ def test_one_producer_not_allowed(self) -> None: assert manager.tx_storage.get_block_count() == 1 def test_two_producers(self) -> None: - signers = get_signer(), get_signer() - signer1, signer2 = sorted(signers, key=lambda signer: get_public_key_bytes_compressed(signer.get_public_key())) + signer1, signer2 = get_signer(), get_signer() signer_id1, signer_id2 = signer1._signer_id, signer2._signer_id self.simulator.settings = get_settings(signer1, signer2, time_between_blocks=10) manager1 = self._get_manager(signer1) @@ -193,8 +206,7 @@ def test_two_producers(self) -> None: _assert_block_in_turn(blocks_manager2[0], signer2) def test_producer_leave_and_comeback(self) -> None: - signers = get_signer(), get_signer() - signer1, signer2 = sorted(signers, key=lambda signer: get_public_key_bytes_compressed(signer.get_public_key())) + signer1, signer2 = get_signer(), get_signer() signer_id1, signer_id2 = signer1._signer_id, signer2._signer_id self.simulator.settings = get_settings(signer1, signer2, time_between_blocks=10) @@ -305,12 +317,9 @@ def test_existing_storage(self) -> None: ) def test_new_signer_added(self) -> None: - signers = get_signer(), get_signer() - key_and_signer1, key_and_signer2 = sorted( - (get_public_key_bytes_compressed(signer.get_public_key()), signer) for signer in signers - ) - key1, signer1 = key_and_signer1 - key2, signer2 = key_and_signer2 + signer1, signer2 = get_signer(), get_signer() + key1 = get_public_key_bytes_compressed(signer1.get_public_key()) + key2 = get_public_key_bytes_compressed(signer2.get_public_key()) signer_settings1 = PoaSignerSettings(public_key=key1) signer_settings2 = PoaSignerSettings(public_key=key2, start_height=6, end_height=13) signer_id1 = signer1._signer_id diff --git a/tests/unittest.py b/tests/unittest.py index f92bc0f50..cccc417a9 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -11,8 +11,8 @@ from hathor.builder import BuildArtifacts, Builder from hathor.checkpoint import Checkpoint -from hathor.conf import HathorSettings from hathor.conf.get_settings import get_global_settings +from hathor.conf.settings import HathorSettings from hathor.daa import DifficultyAdjustmentAlgorithm, TestMode from hathor.event import EventManager from hathor.event.storage import EventStorage @@ -33,7 +33,6 @@ logger = get_logger() main = ut_main -settings = HathorSettings() USE_MEMORY_STORAGE = os.environ.get('HATHOR_TEST_MEMORY_STORAGE', 'false').lower() == 'true' @@ -84,11 +83,12 @@ class SyncBridgeParams: class TestBuilder(Builder): __test__ = False - def __init__(self) -> None: + def __init__(self, settings: HathorSettings | None = None) -> None: super().__init__() self.set_network('testnet') # default builder has sync-v2 enabled for tests self.enable_sync_v2() + self.set_settings(settings or get_global_settings()) def build(self) -> BuildArtifacts: artifacts = super().build() @@ -215,7 +215,8 @@ def create_peer( # type: ignore[no-untyped-def] enable_sync_v1, enable_sync_v2 = self._syncVersionFlags(enable_sync_v1, enable_sync_v2) builder = self.get_builder(network) \ - .set_full_verification(full_verification) + .set_full_verification(full_verification) \ + .set_settings(self._settings) if checkpoints is not None: builder.set_checkpoints(checkpoints)