Skip to content

Commit 95251ce

Browse files
Extract poll_oracles_approval function
1 parent f0690d7 commit 95251ce

File tree

2 files changed

+83
-45
lines changed

2 files changed

+83
-45
lines changed

src/common/utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import logging
3+
import time
34
from collections import defaultdict
45
from datetime import datetime, timezone
56
from pathlib import Path
@@ -113,3 +114,18 @@ def add_fields(self, log_record, record, message_dict): # type: ignore
113114
log_record['level'] = log_record['level'].upper()
114115
else:
115116
log_record['level'] = record.levelname
117+
118+
119+
class RateLimiter:
120+
def __init__(self, min_interval: int) -> None:
121+
self.min_interval = min_interval
122+
self.previous_time: float | None = None
123+
124+
async def ensure_interval(self) -> None:
125+
current_time = time.time()
126+
127+
if self.previous_time is not None:
128+
elapsed = current_time - self.previous_time
129+
await asyncio.sleep(self.min_interval - elapsed)
130+
131+
self.previous_time = current_time

src/validators/tasks.py

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import asyncio
21
import logging
3-
import time
42
from typing import Sequence, cast
53

64
from eth_typing import HexStr
@@ -18,8 +16,8 @@
1816
from src.common.harvest import get_harvest_params
1917
from src.common.metrics import metrics
2018
from src.common.tasks import BaseTask
21-
from src.common.typings import HarvestParams
22-
from src.common.utils import get_current_timestamp
19+
from src.common.typings import HarvestParams, OraclesApproval
20+
from src.common.utils import RateLimiter, get_current_timestamp
2321
from src.config.networks import GNOSIS_NETWORKS
2422
from src.config.settings import DEPOSIT_AMOUNT, settings
2523
from src.validators.database import NetworkValidatorCrud
@@ -85,7 +83,7 @@ async def process_block(self, interrupt_handler: InterruptHandler) -> None:
8583
)
8684

8785

88-
# pylint: disable-next=too-many-locals,too-many-branches,too-many-return-statements,too-many-statements
86+
# pylint: disable-next=too-many-locals,too-many-return-statements
8987
async def process_validators(
9088
keystore: BaseKeystore | None,
9189
deposit_data: DepositData | None,
@@ -173,74 +171,98 @@ async def process_validators(
173171

174172
logger.info('Started registration of %d validator(s)', len(validators))
175173

176-
registry_root = None
177-
oracles_request = None
174+
oracles_request, oracles_approval = await poll_oracles_approval(
175+
keystore=keystore,
176+
validators=validators,
177+
multi_proof=multi_proof,
178+
validators_manager_signature=validators_manager_signature,
179+
)
180+
validators_registry_root = Bytes32(Web3.to_bytes(hexstr=oracles_request.validators_root))
181+
182+
tx_hash = await register_validators(
183+
approval=oracles_approval,
184+
multi_proof=multi_proof,
185+
validators=validators,
186+
harvest_params=harvest_params,
187+
validators_registry_root=validators_registry_root,
188+
validators_manager_signature=validators_manager_signature,
189+
)
190+
if tx_hash:
191+
pub_keys = ', '.join([val.public_key for val in validators])
192+
logger.info('Successfully registered validator(s) with public key(s) %s', pub_keys)
193+
194+
return tx_hash
195+
196+
197+
async def get_validators_count_from_vault_assets(harvest_params: HarvestParams | None) -> int:
198+
vault_balance = await get_withdrawable_assets(harvest_params)
199+
if settings.network in GNOSIS_NETWORKS:
200+
# apply GNO -> mGNO exchange rate
201+
vault_balance = convert_to_mgno(vault_balance)
202+
203+
metrics.stakeable_assets.set(int(vault_balance))
204+
205+
# calculate number of validators that can be registered
206+
validators_count = vault_balance // DEPOSIT_AMOUNT
207+
return validators_count
208+
209+
210+
async def poll_oracles_approval(
211+
keystore: BaseKeystore | None,
212+
validators: Sequence[Validator],
213+
multi_proof: MultiProof[tuple[bytes, int]] | None = None,
214+
validators_manager_signature: HexStr | None = None,
215+
) -> tuple[ApprovalRequest, OraclesApproval]:
216+
"""
217+
Polls oracles for approval of validator registration
218+
"""
219+
previous_registry_root: Bytes32 | None = None
220+
oracles_request: ApprovalRequest | None = None
178221
protocol_config = await get_protocol_config()
179-
deadline = get_current_timestamp() + protocol_config.signature_validity_period
222+
deadline: int | None = None
223+
180224
approvals_min_interval = 1
225+
rate_limiter = RateLimiter(approvals_min_interval)
181226

182227
while True:
183-
approval_start_time = time.time()
228+
# Keep min interval between requests
229+
await rate_limiter.ensure_interval()
230+
231+
# Create new approvals request or reuse the previous one
232+
current_registry_root = await validators_registry_contract.get_registry_root()
233+
logger.debug('Fetched validators registry root: %s', Web3.to_hex(current_registry_root))
184234

185-
latest_registry_root = await validators_registry_contract.get_registry_root()
186235
current_timestamp = get_current_timestamp()
187236
if (
188-
not registry_root
189-
or registry_root != latest_registry_root
237+
oracles_request is None
238+
or previous_registry_root is None
239+
or previous_registry_root != current_registry_root
240+
or deadline is None
190241
or deadline <= current_timestamp
191242
):
192-
registry_root = latest_registry_root
193243
deadline = current_timestamp + protocol_config.signature_validity_period
194-
logger.debug('Fetched latest validators registry root: %s', Web3.to_hex(registry_root))
195244

196245
oracles_request = await create_approval_request(
197246
protocol_config=protocol_config,
198247
keystore=keystore,
199248
validators=validators,
200-
registry_root=registry_root,
249+
registry_root=current_registry_root,
201250
multi_proof=multi_proof,
202251
deadline=deadline,
203252
validators_manager_signature=validators_manager_signature,
204253
)
254+
previous_registry_root = current_registry_root
205255

256+
# Send approval requests
206257
try:
207258
oracles_approval = await send_approval_requests(protocol_config, oracles_request)
208-
break
259+
return oracles_request, oracles_approval
209260
except NotEnoughOracleApprovalsError as e:
210261
logger.error(
211262
'Not enough oracle approvals for validator registration: %d. Threshold is %d.',
212263
e.num_votes,
213264
e.threshold,
214265
)
215-
approvals_time = time.time() - approval_start_time
216-
await asyncio.sleep(approvals_min_interval - approvals_time)
217-
218-
tx_hash = await register_validators(
219-
approval=oracles_approval,
220-
multi_proof=multi_proof,
221-
validators=validators,
222-
harvest_params=harvest_params,
223-
validators_registry_root=registry_root,
224-
validators_manager_signature=validators_manager_signature,
225-
)
226-
if tx_hash:
227-
pub_keys = ', '.join([val.public_key for val in validators])
228-
logger.info('Successfully registered validator(s) with public key(s) %s', pub_keys)
229-
230-
return tx_hash
231-
232-
233-
async def get_validators_count_from_vault_assets(harvest_params: HarvestParams | None) -> int:
234-
vault_balance = await get_withdrawable_assets(harvest_params)
235-
if settings.network in GNOSIS_NETWORKS:
236-
# apply GNO -> mGNO exchange rate
237-
vault_balance = convert_to_mgno(vault_balance)
238-
239-
metrics.stakeable_assets.set(int(vault_balance))
240-
241-
# calculate number of validators that can be registered
242-
validators_count = vault_balance // DEPOSIT_AMOUNT
243-
return validators_count
244266

245267

246268
# pylint: disable-next=too-many-arguments,too-many-locals

0 commit comments

Comments
 (0)