1+ import copy
12import enum
3+ import io
24import logging
35import time
46from typing import Self
79from attrs import define
810from eth_account ._utils .signing import to_standard_v
911from eth_keys .datatypes import Signature as EthSignature
10- from py_flare_common .fsp .epoch .epoch import RewardEpoch
12+ from py_flare_common .fsp .epoch .epoch import RewardEpoch , VotingEpoch
1113from py_flare_common .fsp .messaging import (
1214 parse_generic_tx ,
1315 parse_submit1_tx ,
2224from web3 ._utils .events import get_event_data
2325from web3 .middleware import ExtraDataToPOAMiddleware
2426
27+ from configuration .config import ChainId
2528from configuration .types import Configuration
2629from observer .reward_epoch_manager import (
2730 Entity ,
4144)
4245
4346LOGGER = logging .getLogger (__name__ )
44- logging .basicConfig (level = "INFO" )
45- LOGGER .info ("initialized" )
47+ logging .basicConfig (
48+ format = "%(asctime)s\t %(levelname)s\t %(name)s\t %(message)s" ,
49+ level = "INFO" ,
50+ )
4651
4752
4853class Signature (EthSignature ):
@@ -189,6 +194,9 @@ async def get_signing_policy_events(
189194class IssueLevel (enum .Enum ):
190195 DEBUG = 10
191196 INFO = 20
197+ WARNING = 30
198+ ERROR = 40
199+ CRITICAL = 50
192200
193201
194202@define
@@ -197,6 +205,60 @@ class Issue:
197205 message : str
198206
199207
208+ @define
209+ class MessageBuilder :
210+ network : int | None = None
211+ round : VotingEpoch | None = None
212+ protocol : int | None = None
213+ message : str | None = None
214+
215+ def copy (self ) -> Self :
216+ return copy .copy (self )
217+
218+ def build (self ) -> str :
219+ assert self .message is not None
220+
221+ s = io .StringIO ()
222+
223+ if self .network is not None :
224+ network = ChainId .id_to_name (self .network )
225+ s .write (f"network:{ network } " )
226+
227+ if self .round is not None :
228+ s .write (f"round:{ self .round .id } " )
229+
230+ if self .protocol is not None :
231+ # TODO:(matej) make an enum like class like ChainId
232+ assert self .protocol in [100 , 200 ]
233+ protocol = "ftso" if self .protocol == 100 else "fdc"
234+ s .write (f"protocol:{ protocol } " )
235+
236+ s .write (self .message )
237+
238+ s .seek (0 )
239+ return s .read ()
240+
241+ def build_with_message (self , m : str ) -> str :
242+ return self .copy ().add_message (m ).build ()
243+
244+ def add_network (self , n : int ) -> Self :
245+ assert n in ChainId .all ()
246+ self .network = n
247+ return self
248+
249+ def add_round (self , n : VotingEpoch ) -> Self :
250+ self .round = n
251+ return self
252+
253+ def add_protocol (self , n : int ) -> Self :
254+ self .protocol = n
255+ return self
256+
257+ def add_message (self , m : str ) -> Self :
258+ self .message = m
259+ return self
260+
261+
200262def log_issue (config , issue : Issue ):
201263 LOGGER .log (issue .level .value , issue .message )
202264 notify_discord (config , issue .level .name + " " + issue .message )
@@ -224,7 +286,14 @@ def extract[T](
224286 return latest
225287
226288
227- def validate_ftso (round : VotingRound , entity : Entity ):
289+ def validate_ftso (round : VotingRound , entity : Entity , config : Configuration ):
290+ mb = (
291+ MessageBuilder ()
292+ .add_network (config .chain_id )
293+ .add_round (round .voting_epoch )
294+ .add_protocol (100 )
295+ )
296+
228297 epoch = round .voting_epoch
229298 ftso = round .ftso
230299 finalization = ftso .finalization
@@ -259,19 +328,15 @@ def validate_ftso(round: VotingRound, entity: Entity):
259328 issues .append (
260329 Issue (
261330 IssueLevel .INFO ,
262- f "no submit1 transaction for ftso in round { epoch . id } " ,
331+ mb . build_with_message ( "no submit1 transaction" ) ,
263332 )
264333 )
265334
266335 if s1 and not s2 :
267336 issues .append (
268337 Issue (
269- # TODO:(matej) change level to critical
270- IssueLevel .INFO ,
271- (
272- "no submit2 transaction for ftso in round"
273- f"{ epoch .id } , causing reveal offence"
274- ),
338+ IssueLevel .CRITICAL ,
339+ mb .build_with_message ("no submit2 transaction, causing reveal offence" ),
275340 ),
276341 )
277342
@@ -283,13 +348,11 @@ def validate_ftso(round: VotingRound, entity: Entity):
283348 if indices :
284349 issues .append (
285350 Issue (
286- # TODO:(matej) change level to warning
287- IssueLevel .INFO ,
288- (
289- "submit 2 had 'None' value for feeds on indices "
290- f"{ ', ' .join (indices )} in round { epoch .id } "
351+ IssueLevel .WARNING ,
352+ mb .build_with_message (
353+ f"submit 2 had 'None' on indices { ', ' .join (indices )} "
291354 ),
292- ),
355+ )
293356 )
294357
295358 if s1 and s2 :
@@ -303,21 +366,18 @@ def validate_ftso(round: VotingRound, entity: Entity):
303366 if submit_1 [0 ].payload .commit_hash .hex () != hashed :
304367 issues .append (
305368 Issue (
306- # TODO:(matej) change level to critical
307- IssueLevel .INFO ,
308- (
309- "commit hash and reveal didn't match in round "
310- f"{ epoch .id } , causing reveal offence"
369+ IssueLevel .CRITICAL ,
370+ mb .build_with_message (
371+ "commit hash and reveal didn't match, causing reveal offence"
311372 ),
312373 ),
313374 )
314375
315376 if not ss :
316377 issues .append (
317378 Issue (
318- # TODO:(matej) change level to warning
319- IssueLevel .INFO ,
320- ("no submit signatures transaction for ftso in round " f"{ epoch .id } " ),
379+ IssueLevel .ERROR ,
380+ mb .build_with_message ("no submit signatures transaction" ),
321381 ),
322382 )
323383
@@ -330,19 +390,24 @@ def validate_ftso(round: VotingRound, entity: Entity):
330390 if addr != entity .signing_policy_address :
331391 issues .append (
332392 Issue (
333- # TODO:(matej) change level to warning
334- IssueLevel .INFO ,
335- (
336- "submit signatures signature doesn't match finalization for "
337- f"ftso in round { epoch .id } "
393+ IssueLevel .ERROR ,
394+ mb .build_with_message (
395+ "submit signatures signature doesn't match finalization"
338396 ),
339397 ),
340398 )
341399
342400 return issues
343401
344402
345- def validate_fdc (round : VotingRound , entity : Entity ):
403+ def validate_fdc (round : VotingRound , entity : Entity , config : Configuration ):
404+ mb = (
405+ MessageBuilder ()
406+ .add_network (config .chain_id )
407+ .add_round (round .voting_epoch )
408+ .add_protocol (200 )
409+ )
410+
346411 epoch = round .voting_epoch
347412 fdc = round .fdc
348413 finalization = fdc .finalization
@@ -380,34 +445,31 @@ def validate_fdc(round: VotingRound, entity: Entity):
380445 if not s2 :
381446 issues .append (
382447 Issue (
383- IssueLevel .INFO ,
384- f "no submit2 transaction for fdc in round { epoch . id } " ,
448+ IssueLevel .ERROR ,
449+ mb . build_with_message ( "no submit2 transaction" ) ,
385450 ),
386451 )
387452
388453 if s2 :
389- # TODO:(matej) analize request array and report unprovden errors
454+ # TODO:(matej) analize request array and report unproven errors
390455 ...
391456
392457 if s2 and not ss :
393458 # TODO:(matej) check if submit2 bitvote dominated consensus bitvote
394459 issues .append (
395460 Issue (
396- # TODO:(matej) change level to critical
397- IssueLevel .INFO ,
398- (
399- "no submit signatures transaction for fdc in round"
400- f"{ epoch .id } , causing reveal offence"
461+ IssueLevel .CRITICAL ,
462+ mb .build_with_message (
463+ "no submit signatures transaction, causing reveal offence"
401464 ),
402465 ),
403466 )
404467
405- if not ss :
468+ if not s2 and not ss :
406469 issues .append (
407470 Issue (
408- # TODO:(matej) change level to critical
409- IssueLevel .INFO ,
410- ("no submit signatures transaction for fdc in round " f"{ epoch .id } " ),
471+ IssueLevel .ERROR ,
472+ mb .build_with_message ("no submit signatures transaction" ),
411473 ),
412474 )
413475
@@ -420,11 +482,9 @@ def validate_fdc(round: VotingRound, entity: Entity):
420482 if addr != entity .signing_policy_address :
421483 issues .append (
422484 Issue (
423- # TODO:(matej) change level to warning
424- IssueLevel .INFO ,
425- (
426- "submit signatures signature doesn't match finalization for "
427- f"fdc in round { epoch .id } "
485+ IssueLevel .ERROR ,
486+ mb .build_with_message (
487+ "submit signatures signature doesn't match finalization"
428488 ),
429489 ),
430490 )
@@ -438,6 +498,19 @@ async def observer_loop(config: Configuration) -> None:
438498 middleware = [ExtraDataToPOAMiddleware ],
439499 )
440500
501+ # log_issue(
502+ # config,
503+ # Issue(
504+ # IssueLevel.INFO,
505+ # MessageBuilder()
506+ # .add_network(config.chain)
507+ # .add_protocol(100)
508+ # .add_round(VotingEpoch(12, None))
509+ # .build_with_message("testing message"),
510+ # ),
511+ # )
512+ # return
513+
441514 # reasignments for quick access
442515 ve = config .epoch .voting_epoch
443516 # re = config.epoch.reward_epoch
@@ -476,6 +549,17 @@ async def observer_loop(config: Configuration) -> None:
476549
477550 # set up target address from config
478551 tia = w .to_checksum_address (config .identity_address )
552+ log_issue (
553+ config ,
554+ Issue (
555+ IssueLevel .INFO ,
556+ MessageBuilder ()
557+ .add_network (config .chain_id )
558+ .build_with_message (
559+ f"Initialized observer for identity_address={ tia } " ,
560+ ),
561+ ),
562+ )
479563 # target_voter = signing_policy.entity_mapper.by_identity_address[tia]
480564 # notify_discord(
481565 # config,
@@ -664,11 +748,11 @@ async def observer_loop(config: Configuration) -> None:
664748 rounds = vrm .finalize (block_data )
665749 for r in rounds :
666750 for i in validate_ftso (
667- r , signing_policy .entity_mapper .by_identity_address [tia ]
751+ r , signing_policy .entity_mapper .by_identity_address [tia ], config
668752 ):
669753 log_issue (config , i )
670754 for i in validate_fdc (
671- r , signing_policy .entity_mapper .by_identity_address [tia ]
755+ r , signing_policy .entity_mapper .by_identity_address [tia ], config
672756 ):
673757 log_issue (config , i )
674758
0 commit comments