Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion hathor/p2p/sync_v2/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
MAX_GET_TRANSACTIONS_BFS_LEN: int = 8
MAX_MEMPOOL_STATUS_TIPS: int = 20

RUN_SYNC_MAIN_LOOP_INTERVAL = 1 # second(s)


class _HeightInfo(NamedTuple):
height: int
Expand Down Expand Up @@ -232,7 +234,7 @@ def start(self) -> None:
if self._started:
raise Exception('NodeSyncBlock is already running')
self._started = True
self._lc_run.start(5)
self._lc_run.start(RUN_SYNC_MAIN_LOOP_INTERVAL)

def stop(self) -> None:
if not self._started:
Expand Down
77 changes: 50 additions & 27 deletions tests/poa/test_poa_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from hathor.consensus import poa
from hathor.consensus.consensus_settings import PoaSettings, PoaSignerSettings
from hathor.consensus.poa import PoaSigner
from hathor.consensus.poa.poa_signer import PoaSignerId
from hathor.crypto.util import get_address_b58_from_public_key_bytes, get_public_key_bytes_compressed
from hathor.manager import HathorManager
from hathor.simulator import FakeConnection
Expand Down Expand Up @@ -61,7 +62,7 @@ def _assert_block_in_turn(block: PoaBlock, signer: PoaSigner) -> None:

def _assert_height_weight_signer_id(
vertices: Iterator[BaseTransaction],
expected: list[tuple[int, float, bytes]]
expected: list[tuple[int, float, PoaSignerId]]
) -> None:
non_voided_blocks: list[tuple[int, float, bytes]] = []

Expand Down Expand Up @@ -248,58 +249,80 @@ def test_producer_leave_and_comeback(self) -> None:
signer_id1, signer_id2 = signer1._signer_id, signer2._signer_id
self.simulator.settings = get_settings(signer1, signer2, time_between_blocks=10)

expected = [
# Before manager2 joins, only manager1 produces blocks
(1, poa.BLOCK_WEIGHT_OUT_OF_TURN, signer_id1),
(2, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(3, poa.BLOCK_WEIGHT_OUT_OF_TURN, signer_id1),
# When manager2 joins, both of them start taking turns
# But manager2 must sync first.
(4, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
# Here manager2 has already synced.
(5, poa.BLOCK_WEIGHT_IN_TURN, signer_id2),
(6, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(7, poa.BLOCK_WEIGHT_IN_TURN, signer_id2),
(8, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(9, poa.BLOCK_WEIGHT_IN_TURN, signer_id2),
(10, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(11, poa.BLOCK_WEIGHT_IN_TURN, signer_id2),
(12, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
# manager2 leaves so manager1 produces all the next blocks
(13, poa.BLOCK_WEIGHT_OUT_OF_TURN, signer_id1),
(14, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(15, poa.BLOCK_WEIGHT_OUT_OF_TURN, signer_id1),
# manager2 comes back again, so both of them take turns again
(16, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(17, poa.BLOCK_WEIGHT_IN_TURN, signer_id2),
(18, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
]

# here we create a situation with an intermittent producer, testing that the other producer produces blocks
# out of turn
manager1 = self._get_manager(signer1)
manager1.allow_mining_without_peers()
self.simulator.run(50)

assert manager1.tx_storage.get_block_count() == 4
_assert_height_weight_signer_id(
manager1.tx_storage.get_all_transactions(),
expected[:3],
)

manager2 = self._get_manager(signer2)
connection = FakeConnection(manager1, manager2)
self.simulator.add_connection(connection)
self.simulator.run(80)

assert manager1.tx_storage.get_block_count() == 14
_assert_height_weight_signer_id(
manager1.tx_storage.get_all_transactions(),
expected[:12],
)

manager2.stop()
connection.disconnect(Failure(Exception('testing')))
self.simulator.remove_connection(connection)
self.simulator.run(70)

assert manager1.tx_storage.get_block_count() == 17
_assert_height_weight_signer_id(
manager1.tx_storage.get_all_transactions(),
expected[:15],
)

assert not manager2.can_start_mining()
self.simulator.add_connection(connection)
connection.reconnect()
manager2.start()
self.simulator.run(30)

assert manager1.tx_storage.get_block_count() == 19
assert manager2.tx_storage.get_block_count() == 19
assert manager1.tx_storage.get_block_count() == 20
assert manager2.tx_storage.get_block_count() == 20
assert manager1.tx_storage.get_best_block_tips() == manager2.tx_storage.get_best_block_tips()

_assert_height_weight_signer_id(
manager1.tx_storage.get_all_transactions(),
[
# Before manager2 joins, only manager1 produces blocks
(1, poa.BLOCK_WEIGHT_OUT_OF_TURN, signer_id1),
(2, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(3, poa.BLOCK_WEIGHT_OUT_OF_TURN, signer_id1),
(4, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(5, poa.BLOCK_WEIGHT_OUT_OF_TURN, signer_id1),
(6, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
# When manager2 joins, both of them start taking turns
(7, poa.BLOCK_WEIGHT_IN_TURN, signer_id2),
(8, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(9, poa.BLOCK_WEIGHT_IN_TURN, signer_id2),
(10, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(11, poa.BLOCK_WEIGHT_IN_TURN, signer_id2),
(12, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
# manager2 leaves so manager1 produces all the next blocks
(13, poa.BLOCK_WEIGHT_OUT_OF_TURN, signer_id1),
(14, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(15, poa.BLOCK_WEIGHT_OUT_OF_TURN, signer_id1),
# manager2 comes back again, so both of them take turns again
(16, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
(17, poa.BLOCK_WEIGHT_IN_TURN, signer_id2),
(18, poa.BLOCK_WEIGHT_IN_TURN, signer_id1),
]
expected,
)

@pytest.mark.skipif(not HAS_ROCKSDB, reason='requires python-rocksdb')
Expand Down