From 77dd62377393645ada383929974a231624331a51 Mon Sep 17 00:00:00 2001 From: madlabman <10616301+madlabman@users.noreply.github.com> Date: Sat, 23 Nov 2024 01:43:17 +0800 Subject: [PATCH 1/3] fix: do not fail on missing digests for stucks In the case the initial epoch is set in the way the starting epoch of a reporting frame is set before enactment of a vote connecting CSM to the staking router, the existing sanity check prevents oracles from building report. --- src/modules/csm/csm.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/modules/csm/csm.py b/src/modules/csm/csm.py index 190199dcc..ff7e52d43 100644 --- a/src/modules/csm/csm.py +++ b/src/modules/csm/csm.py @@ -308,10 +308,13 @@ def stuck_operators(self, blockstamp: ReferenceBlockStamp) -> set[NodeOperatorId blockstamp.slot_number, ) ) - digests = self.w3.lido_validators.get_lido_node_operators_by_modules(l_blockstamp).get(self.module_id) - if digests is None: - raise InconsistentData(f"No Node Operators digests found for {self.module_id=}") - stuck.update(no.id for no in digests if no.stuck_validators_count > 0) + + nos_by_module = self.w3.lido_validators.get_lido_node_operators_by_modules(l_blockstamp) + if self.module_id in nos_by_module: + stuck.update(no.id for no in nos_by_module[self.module_id] if no.stuck_validators_count > 0) + else: + logger.warning("No CSM digest at blockstamp=%s, module was not added yet?", l_blockstamp) + stuck.update( self.w3.csm.get_operators_with_stucks_in_range( l_blockstamp.block_hash, From 09b1b9592e5e5966081f7fe44ce41184514467c6 Mon Sep 17 00:00:00 2001 From: madlabman <10616301+madlabman@users.noreply.github.com> Date: Sat, 23 Nov 2024 02:06:42 +0800 Subject: [PATCH 2/3] test: add unit for stuck_operators function --- tests/modules/csm/test_csm_module.py | 51 +++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/tests/modules/csm/test_csm_module.py b/tests/modules/csm/test_csm_module.py index a8ab72fd2..7d48e6e70 100644 --- a/tests/modules/csm/test_csm_module.py +++ b/tests/modules/csm/test_csm_module.py @@ -16,7 +16,7 @@ from src.providers.ipfs import CIDv0, CID from src.types import EpochNumber, NodeOperatorId, SlotNumber, StakingModuleId, ValidatorIndex from src.web3py.extensions.csm import CSM -from tests.factory.blockstamp import ReferenceBlockStampFactory +from tests.factory.blockstamp import BlockStampFactory, ReferenceBlockStampFactory from tests.factory.configs import ChainConfigFactory, FrameConfigFactory @@ -83,6 +83,55 @@ def test_stuck_operators(module: CSOracle, csm: CSM): assert stuck == {NodeOperatorId(2), NodeOperatorId(4), NodeOperatorId(5), NodeOperatorId(6), NodeOperatorId(1337)} +def test_stuck_operators_left_border_before_enact(module: CSOracle, csm: CSM, caplog: pytest.LogCaptureFixture): + module.module = Mock() # type: ignore + module.module_id = StakingModuleId(3) + module.w3.cc = Mock() + module.w3.lido_validators = Mock() + module.w3.lido_contracts = Mock() + module.w3.lido_validators.get_lido_node_operators_by_modules = Mock( + return_value={ + 1: { + type('NodeOperator', (object,), {'id': 0, 'stuck_validators_count': 0})(), + type('NodeOperator', (object,), {'id': 1, 'stuck_validators_count': 0})(), + type('NodeOperator', (object,), {'id': 2, 'stuck_validators_count': 1})(), + type('NodeOperator', (object,), {'id': 3, 'stuck_validators_count': 0})(), + type('NodeOperator', (object,), {'id': 4, 'stuck_validators_count': 100500})(), + type('NodeOperator', (object,), {'id': 5, 'stuck_validators_count': 100})(), + type('NodeOperator', (object,), {'id': 6, 'stuck_validators_count': 0})(), + }, + 2: {}, + } + ) + + module.w3.csm.get_operators_with_stucks_in_range = Mock( + return_value=[ + NodeOperatorId(2), + NodeOperatorId(4), + NodeOperatorId(6), + ] + ) + + module.current_frame_range = Mock(return_value=(69, 100)) + module.converter = Mock() + module.converter.get_epoch_first_slot = Mock(return_value=lambda epoch: epoch * 32) + + l_blockstamp = BlockStampFactory.build() + blockstamp = BlockStampFactory.build() + + with patch('src.modules.csm.csm.build_blockstamp', return_value=l_blockstamp): + with patch('src.modules.csm.csm.get_next_non_missed_slot', return_value=Mock()): + stuck = module.stuck_operators(blockstamp=blockstamp) + + assert stuck == { + NodeOperatorId(2), + NodeOperatorId(4), + NodeOperatorId(6), + } + + assert caplog.messages[0].startswith("No CSM digest at blockstamp") + + def test_calculate_distribution(module: CSOracle, csm: CSM): csm.fee_distributor.shares_to_distribute = Mock(return_value=10_000) csm.oracle.perf_leeway_bp = Mock(return_value=500) From 0cc6ca8bcdb08d82737f19e8e40148033eb65092 Mon Sep 17 00:00:00 2001 From: madlabman <10616301+madlabman@users.noreply.github.com> Date: Sat, 23 Nov 2024 04:04:44 +0800 Subject: [PATCH 3/3] build: bump curl --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 63a73a7fc..b0fde007e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM python:3.12.4-slim as base RUN apt-get update && apt-get install -y --no-install-recommends -qq \ libffi-dev=3.4.4-1 \ g++=4:12.2.0-3 \ - curl=7.88.1-10+deb12u7 \ + curl=7.88.1-10+deb12u8 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/*