Skip to content

Commit 17455ec

Browse files
committed
feat: message builder for consistent issue reporting
1 parent a4b9f0e commit 17455ec

File tree

3 files changed

+136
-52
lines changed

3 files changed

+136
-52
lines changed

configuration/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def get_config() -> Configuration:
7878
config = Configuration(
7979
rpc_url=rpc_url,
8080
identity_address=to_checksum_address(identity_address),
81-
chain=ChainId.id_to_name(chain_id),
81+
chain_id=chain_id,
8282
contracts=Contracts.get_contracts(w),
8383
epoch=get_epoch(chain_id),
8484
discord_webhook=os.environ.get("DISCORD_WEBHOOK"),

configuration/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class Epoch:
182182
@frozen
183183
class Configuration:
184184
identity_address: ChecksumAddress
185-
chain: str
185+
chain_id: int
186186
contracts: Contracts
187187
rpc_url: str
188188
epoch: Epoch

observer/observer.py

Lines changed: 134 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import copy
12
import enum
3+
import io
24
import logging
35
import time
46
from typing import Self
@@ -7,7 +9,7 @@
79
from attrs import define
810
from eth_account._utils.signing import to_standard_v
911
from 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
1113
from py_flare_common.fsp.messaging import (
1214
parse_generic_tx,
1315
parse_submit1_tx,
@@ -22,6 +24,7 @@
2224
from web3._utils.events import get_event_data
2325
from web3.middleware import ExtraDataToPOAMiddleware
2426

27+
from configuration.config import ChainId
2528
from configuration.types import Configuration
2629
from observer.reward_epoch_manager import (
2730
Entity,
@@ -41,8 +44,10 @@
4144
)
4245

4346
LOGGER = 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

4853
class Signature(EthSignature):
@@ -189,6 +194,9 @@ async def get_signing_policy_events(
189194
class 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+
200262
def 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

Comments
 (0)