Skip to content

Commit

Permalink
Print useful information when the chain is not yet started
Browse files Browse the repository at this point in the history
  • Loading branch information
nalepae committed Sep 27, 2023
1 parent c65d931 commit 4cfcc6d
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 5 deletions.
24 changes: 23 additions & 1 deletion eth_validator_watcher/entrypoint.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Entrypoint for the eth-validator-watcher CLI."""

import functools
from os import environ
from pathlib import Path
from time import sleep, time
Expand Down Expand Up @@ -27,21 +28,25 @@
from .slashed_validators import SlashedValidators
from .suboptimal_attestations import process_suboptimal_attestations
from .utils import (
CHUCK_NORRIS,
MISSED_BLOCK_TIMEOUT_SEC,
NB_SECOND_PER_SLOT,
NB_SLOT_PER_EPOCH,
SLOT_FOR_MISSED_ATTESTATIONS_PROCESS,
SLOT_FOR_REWARDS_PROCESS,
LimitedDict,
Slack,
convert_seconds_to_dhms,
eth1_address_0x_prefixed,
get_our_pubkeys,
slots,
write_liveness_file,
)
from .web3signer import Web3Signer

Status = Validators.DataItem.StatusEnum
print = functools.partial(print, flush=True)

Status = Validators.DataItem.StatusEnum

app = typer.Typer(add_completion=False)

Expand Down Expand Up @@ -238,6 +243,23 @@ def _handler(
genesis = beacon.get_genesis()

for slot, slot_start_time_sec in slots(genesis.data.genesis_time):
if slot < 0:
chain_start_in_sec = -slot * NB_SECOND_PER_SLOT
days, hours, minutes, seconds = convert_seconds_to_dhms(chain_start_in_sec)

print(
f"⏱️ The chain will start in {days:2} days, {hours:2} hours, "
f"{minutes:2} minutes and {seconds:2} seconds."
)

if slot % NB_SLOT_PER_EPOCH == 0:
print(f"💪 {CHUCK_NORRIS[slot%len(CHUCK_NORRIS)]}")

if liveness_file is not None:
write_liveness_file(liveness_file)

continue

epoch = slot // NB_SLOT_PER_EPOCH
slot_in_epoch = slot % NB_SLOT_PER_EPOCH

Expand Down
36 changes: 33 additions & 3 deletions eth_validator_watcher/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,29 @@
ETH1_ADDRESS_LEN = 40
ETH2_ADDRESS_LEN = 96

CHUCK_NORRIS = [
"Chuck Norris doesn't stake Ethers; he stares at the blockchain, and it instantly "
"produces new coins.",
"When Chuck Norris sends Ethers, it doesn't need confirmations. The Ethereum "
"network just knows better than to mess with Chuck.",
"Chuck Norris once hacked into a smart contract without using a computer. He just "
"stared at the code, and it fixed itself.",
"Ethereum's gas fees are afraid of Chuck Norris. They lower themselves just to "
"avoid his wrath.",
"Chuck Norris doesn't need a private key to access his Ethereum wallet. He just "
"flexes his biceps, and it opens.",
"When Chuck Norris trades on a decentralized exchange, the price slippage goes in "
"his favor, no matter what.",
"Vitalik Buterin once challenged Chuck Norris to a coding contest. Chuck won by "
"writing Ethereum's whitepaper with his eyes closed.",
"Chuck Norris's Ethereum nodes are so fast that they can process transactions "
"before they even happen.",
'The Ethereum community calls Chuck Norris the "Smart Contract Whisperer" '
"because he can make any contract do his bidding.",
"When Chuck Norris checks his Ethereum balance, the wallet interface just says, "
'"Infinite."',
]

keys_count = Gauge(
"keys_count",
"Keys count",
Expand Down Expand Up @@ -207,9 +230,7 @@ def send_message(self, message: str) -> None:


def slots(genesis_time_sec: int) -> Iterator[Tuple[int, int]]:
# max(0, ...) is used to avoid negative slot number if genesis time is not yet
# reached
next_slot = max(0, int((time() - genesis_time_sec) / NB_SECOND_PER_SLOT) + 1)
next_slot = int((time() - genesis_time_sec) / NB_SECOND_PER_SLOT) + 1

try:
while True:
Expand All @@ -224,6 +245,15 @@ def slots(genesis_time_sec: int) -> Iterator[Tuple[int, int]]:
pass # pragma: no cover


def convert_seconds_to_dhms(seconds: int) -> tuple[int, int, int, int]:
# Calculate days, hours, minutes, and seconds
days, seconds = divmod(seconds, 86400) # 1 day = 24 hours * 60 minutes * 60 seconds
hours, seconds = divmod(seconds, 3600) # 1 hour = 60 minutes * 60 seconds
minutes, seconds = divmod(seconds, 60) # 1 minute = 60 seconds

return days, hours, minutes, seconds


def eth1_address_0x_prefixed(address: str) -> str:
if not re.match(f"^(0x)?[0-9a-fA-F]{{{ETH1_ADDRESS_LEN}}}$", address):
raise ValueError(f"Invalid ETH1 address: {address}")
Expand Down
51 changes: 50 additions & 1 deletion tests/entrypoint/test__handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,55 @@ def start_http_server(_: int) -> None:
)


def test_invalid_chain_not_ready() -> None:
class Beacon:
def __init__(self, url: str) -> None:
assert url == "http://localhost:5052"

def get_genesis(self) -> Genesis:
return Genesis(
data=Genesis.Data(
genesis_time=0,
)
)

def get_our_pubkeys(pubkeys_file_path: Path, web3signer: None) -> set[str]:
return {"0x12345", "0x67890"}

def slots(genesis_time: int) -> Iterator[Tuple[(int, int)]]:
assert genesis_time == 0
yield -32, 1664

def convert_seconds_to_dhms(seconds: int) -> Tuple[int, int, int, int]:
assert seconds == 384
return 42, 42, 42, 42

def write_liveness_file(liveness_file: Path) -> None:
assert liveness_file == Path("/path/to/liveness")

def start_http_server(_: int) -> None:
pass

entrypoint.get_our_pubkeys = get_our_pubkeys
entrypoint.Beacon = Beacon
entrypoint.slots = slots
entrypoint.convert_seconds_to_dhms = convert_seconds_to_dhms
entrypoint.write_liveness_file = write_liveness_file
entrypoint.start_http_server = start_http_server

_handler(
beacon_url="http://localhost:5052",
execution_url=None,
pubkeys_file_path=Path("/path/to/pubkeys"),
web3signer_url=None,
fee_recipient=None,
slack_channel=None,
beacon_type=BeaconType.OLD_TEKU,
relays_url=[],
liveness_file=Path("/path/to/liveness"),
)


@freeze_time("2023-01-01 00:00:00", auto_tick_seconds=15)
def test_nominal() -> None:
class Beacon:
Expand Down Expand Up @@ -263,7 +312,7 @@ def process_rewards(
epoch: int,
net_epoch2active_idx2val: dict[int, Validator],
our_epoch2active_idx2val: dict[int, Validator],
):
) -> None:
assert isinstance(beacon, Beacon)
assert isinstance(beacon_type, BeaconType)
assert epoch == 1
Expand Down
8 changes: 8 additions & 0 deletions tests/utils/test_convert_seconds_to_dhms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from eth_validator_watcher.utils import convert_seconds_to_dhms


def test_convert_secondes_to_dhms() -> None:
assert convert_seconds_to_dhms(0) == (0, 0, 0, 0)
assert convert_seconds_to_dhms(61) == (0, 0, 1, 1)
assert convert_seconds_to_dhms(3601) == (0, 1, 0, 1)
assert convert_seconds_to_dhms(86462) == (1, 0, 1, 2)

0 comments on commit 4cfcc6d

Please sign in to comment.