-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/val 1404 eip 7251 head watcher alerts for new el requests #58
base: develop
Are you sure you want to change the base?
Changes from all commits
35442e6
18bcefd
4bd5b5f
7339592
13deb61
be7d453
ba8e56d
3e7617c
fb1a38c
824d994
d652744
f8c7d92
73e1ac6
33475b5
c5d30f7
a17b7d5
0e52491
149d827
64ed4c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -131,3 +131,6 @@ dmypy.json | |
|
||
# IDE | ||
.idea/ | ||
|
||
# Docker | ||
.volumes |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import logging | ||
|
||
from unsync import unsync | ||
|
||
from src.alerts.common import CommonAlert | ||
from src.handlers.handler import WatcherHandler | ||
from src.handlers.helpers import beaconchain | ||
from src.metrics.prometheus.duration_meter import duration_meter | ||
from src.providers.consensus.typings import FullBlockInfo | ||
|
||
logger = logging.getLogger() | ||
|
||
|
||
class ConsolidationHandler(WatcherHandler): | ||
@unsync | ||
@duration_meter() | ||
def handle(self, watcher, head: FullBlockInfo): | ||
if not head.message.body.execution_requests or not head.message.body.execution_requests.consolidations: | ||
logger.info({"msg": f"No consolidation requests in block [{head.message.slot}]"}) | ||
return | ||
|
||
slot = head.message.slot | ||
for consolidation in head.message.body.execution_requests.consolidations: | ||
alert, summary = None, "" | ||
if consolidation.source_address in watcher.valid_withdrawal_addresses: | ||
alert = CommonAlert(name="HeadWatcherConsolidationSourceWithdrawalAddress", severity="critical") | ||
summary = "‼️⛔️Validator consolidation was requested from Withdrawal Vault source address" | ||
elif consolidation.source_pubkey in watcher.user_keys: | ||
alert = CommonAlert(name="HeadWatcherConsolidationUserSourcePubkey", severity="info") | ||
summary = "⚠️Consolidation was requested for our validators" | ||
elif consolidation.target_pubkey in watcher.user_keys: | ||
alert = CommonAlert(name="HeadWatcherConsolidationUserTargetPubkey", severity="info") | ||
summary = "⚠️Someone attempts to consolidate their validators to our validators" | ||
# in the future we should check the type of validator WC: | ||
# if it is 0x02 and source_address == WCs of source validator - It's donation! | ||
|
||
if alert: | ||
description = (f"EL consolidation request source_address='{consolidation.source_address}', " | ||
f"source_pubkey={consolidation.source_pubkey}, " | ||
f"target_pubkey='{consolidation.target_pubkey}'\n" | ||
Comment on lines
+38
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be better to make alert description more informative. So, if the The same logic for the Maybe it's better to have separate description templates for each type of alert, but maybe not. I'm not sure about it. Both decisions are probably OK. |
||
f"Slot: {beaconchain(slot)}") | ||
self.send_alert(watcher, alert.build_body(summary, description)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks suboptimal to send separate alerts for each consolidation. If we have N consolidations of the same type in the head block, the N separate alerts of the same type will be sent. I think it would be better to group alerts of the same type, list all necessary info in the alert description, and send only one alert for each type. Something like it's done in the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import logging | ||
|
||
from unsync import unsync | ||
|
||
from src.alerts.common import CommonAlert | ||
from src.handlers.handler import WatcherHandler | ||
from src.handlers.helpers import beaconchain | ||
from src.metrics.prometheus.duration_meter import duration_meter | ||
from src.providers.consensus.typings import FullBlockInfo | ||
|
||
logger = logging.getLogger() | ||
|
||
|
||
class ElTriggeredExitHandler(WatcherHandler): | ||
@unsync | ||
@duration_meter() | ||
def handle(self, watcher, head: FullBlockInfo): | ||
if not head.message.body.execution_requests or not head.message.body.execution_requests.withdrawals: | ||
logger.debug({"msg": f"No withdrawals requests in block [{head.message.slot}]"}) | ||
return | ||
|
||
slot = head.message.slot | ||
for withdrawal in head.message.body.execution_requests.withdrawals: | ||
alert, summary = None, "" | ||
if withdrawal.source_address in watcher.valid_withdrawal_addresses: | ||
alert = CommonAlert(name="HeadWatcherELWithdrawalFromUserWithdrawalAddress", severity="critical") | ||
summary = "🔗🏃🚪Our validator triggered withdrawal was requested from our Withdrawal Vault address" | ||
elif withdrawal.validator_pubkey in watcher.user_keys: | ||
alert = CommonAlert(name="HeadWatcherUserELWithdrawal", severity="info") | ||
summary = "🔗🏃🚪Our validator triggered withdrawal was requested" | ||
|
||
if alert: | ||
description = (f"EL withdrawals request source_address='{withdrawal.source_address}', " | ||
f"validator_pubkey={withdrawal.validator_pubkey}, " | ||
f"amount='{withdrawal.amount}'\n" | ||
f"Slot: {beaconchain(slot)}") | ||
self.send_alert(watcher, alert.build_body(summary, description)) | ||
Comment on lines
+33
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same thoughts as for the Consolidation alerts. Seems better to print the opeartor name and beaconcha.in links where possible and group alerts of the same type. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from src.variables import NETWORK_NAME | ||
|
||
BEACONCHAIN_URL_TEMPLATE = "[{0}](https://{1}.beaconcha.in/slot/{0})" | ||
|
||
|
||
def beaconchain(slot) -> str: | ||
return BEACONCHAIN_URL_TEMPLATE.format(slot, NETWORK_NAME) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import pytest | ||
|
||
from src.keys_source.base_source import NamedKey | ||
from tests.execution_requests.helpers import gen_random_address | ||
from tests.execution_requests.stubs import TestValidator, WatcherStub | ||
|
||
|
||
@pytest.fixture | ||
def user_keys() -> dict[str, NamedKey]: | ||
return {} | ||
|
||
|
||
@pytest.fixture | ||
def validator(): | ||
return TestValidator.random() | ||
|
||
|
||
@pytest.fixture | ||
def user_validator(user_keys): | ||
random_validator = TestValidator.random() | ||
user_keys[random_validator.pubkey] = NamedKey( | ||
key=random_validator.pubkey, operatorName='Test operator', operatorIndex='1', moduleIndex='1' | ||
) | ||
return random_validator | ||
|
||
|
||
@pytest.fixture | ||
def watcher(user_keys) -> WatcherStub: | ||
return WatcherStub(user_keys=user_keys) | ||
|
||
|
||
@pytest.fixture | ||
def withdrawal_address(watcher: WatcherStub) -> str: | ||
address = gen_random_address() | ||
watcher.valid_withdrawal_addresses.add(address) | ||
return address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably implement a new alert class for these types of events (donations). It might handle two cases: when someone changes 0x01 validator withdrawal credentials to Lido withdrawal credentials, and when someone changes 0x02 validator withdrawal credentials to Lido withdrawal credentials. And we may have two new types of alerts for these events. Let's discuss it in more detail.