Skip to content

Commit 07703f1

Browse files
chore(randomness): pr review
1 parent bee217e commit 07703f1

13 files changed

Lines changed: 233 additions & 118 deletions

File tree

apps/randomness-monitor/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Randomness Monitor
2+
3+
This application monitors the randomness service by directly inspecting the blockchain to verify whether randomness data is included in each block.
4+
5+
The results are stored in a SQLite database and visualized via Grafana on the following dashboard:
6+
7+
🔗 [View Dashboard](https://grafana.happy.tech/goto/BMpaDZbNg?orgId=1)
8+
9+
In addition to visualization, the collected data is used to trigger alerts when the randomness service's performance drops below 95% block coverage.
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
export enum MonitoringResult {
1+
export enum CheckResult {
22
Success = "success",
33
Failure = "failure",
44
}
55

6-
export class Monitoring {
6+
export class Check {
77
readonly blockNumber: bigint
88
readonly blockTimestamp: bigint
9-
readonly result: MonitoringResult
9+
readonly result: CheckResult
1010
readonly errorDescription: string | undefined
1111
readonly value: string | undefined
1212

1313
constructor(
1414
blockNumber: bigint,
1515
blockTimestamp: bigint,
16-
result: MonitoringResult,
16+
result: CheckResult,
1717
errorDescription?: string,
1818
value?: string,
1919
) {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { unknownToError } from "@happy.tech/common"
2+
import { type Result, ResultAsync, err, ok } from "neverthrow"
3+
import { db } from "./db/driver"
4+
import { checkEntityToRow, checkRowToEntity } from "./db/types"
5+
import type { Check } from "./Check"
6+
7+
const PRUNE_BLOCK_TIMESTAMP_THRESHOLD_SECONDS = 60 * 60 * 24 * 30 // 30 days
8+
9+
export class CheckRepository {
10+
async saveCheck(check: Check): Promise<Result<void, Error>> {
11+
const row = checkEntityToRow(check)
12+
13+
return await ResultAsync.fromPromise(db.insertInto("checks").values(row).execute(), unknownToError).map(
14+
() => undefined,
15+
)
16+
}
17+
18+
async findLatestCheck(): Promise<Result<Check | undefined, Error>> {
19+
const result = await ResultAsync.fromPromise(
20+
db.selectFrom("checks").selectAll().orderBy("blockNumber", "desc").limit(1).executeTakeFirst(),
21+
unknownToError,
22+
)
23+
if (result.isErr()) {
24+
return err(result.error)
25+
}
26+
return ok(result.value ? checkRowToEntity(result.value) : undefined)
27+
}
28+
29+
async pruneChecks(blockTimestamp: bigint): Promise<Result<void, Error>> {
30+
return await ResultAsync.fromPromise(
31+
db
32+
.deleteFrom("checks")
33+
.where("blockTimestamp", "<", Number(blockTimestamp) - PRUNE_BLOCK_TIMESTAMP_THRESHOLD_SECONDS)
34+
.execute(),
35+
unknownToError,
36+
).map(() => undefined)
37+
}
38+
}

apps/randomness-monitor/src/MonitorigRepository.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

apps/randomness-monitor/src/db/migrations/Migration20250403123000.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Database } from "../types"
33

44
export async function up(db: Kysely<Database>) {
55
await db.schema
6-
.createTable("monitoring")
6+
.createTable("checks")
77
.addColumn("blockNumber", "integer", (col) => col.notNull())
88
.addColumn("blockTimestamp", "integer", (col) => col.notNull())
99
.addColumn("result", "text", (col) => col.notNull())
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import { Monitoring, type MonitoringResult } from "../Monitoring"
1+
import { Check, type CheckResult } from "../Check"
22

3-
export interface MonitoringRow {
3+
export interface CheckRow {
44
blockNumber: number
55
blockTimestamp: number
6-
result: MonitoringResult
6+
result: CheckResult
77
errorDescription: string | undefined
88
value: string | undefined
99
}
1010

11-
export function monitoringRowToEntity(row: MonitoringRow): Monitoring {
12-
return new Monitoring(
11+
export function checkRowToEntity(row: CheckRow): Check {
12+
return new Check(
1313
BigInt(row.blockNumber),
1414
BigInt(row.blockTimestamp),
1515
row.result,
@@ -18,7 +18,7 @@ export function monitoringRowToEntity(row: MonitoringRow): Monitoring {
1818
)
1919
}
2020

21-
export function monitoringEntityToRow(entity: Monitoring): MonitoringRow {
21+
export function checkEntityToRow(entity: Check): CheckRow {
2222
return {
2323
blockNumber: Number(entity.blockNumber),
2424
blockTimestamp: Number(entity.blockTimestamp),
@@ -29,5 +29,5 @@ export function monitoringEntityToRow(entity: Monitoring): MonitoringRow {
2929
}
3030

3131
export interface Database {
32-
monitoring: MonitoringRow
32+
checks: CheckRow
3333
}

apps/randomness-monitor/src/index.ts

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { bigIntReplacer, unknownToError } from "@happy.tech/common"
2+
import { getUrlProtocol } from "@happy.tech/common"
23
import { abis } from "@happy.tech/contracts/random/anvil"
34
import { type Result, err, ok } from "neverthrow"
45
import { http, type Block, type PublicClient, type Transport, createPublicClient, defineChain, webSocket } from "viem"
5-
import { MonitoringRepository } from "./MonitorigRepository"
6-
import { Monitoring, MonitoringResult } from "./Monitoring"
6+
import { CheckRepository } from "./CheckRepository"
7+
import { Check, CheckResult } from "./Check"
78
import { env } from "./env"
8-
import { getUrlProtocol } from "./utils/getUrlProtocol"
9+
import { logger } from "./utils/logger"
910

1011
/**
1112
* Main monitoring service for tracking blockchain randomness
1213
*/
1314
export class MonitoringService {
14-
private readonly monitoringRepository: MonitoringRepository
15+
private readonly checkRepository: CheckRepository
1516
private readonly viemClient: PublicClient
1617
private readonly protocol: "http" | "websocket"
1718

@@ -20,7 +21,7 @@ export class MonitoringService {
2021
private isProcessing = false
2122

2223
constructor() {
23-
this.monitoringRepository = new MonitoringRepository()
24+
this.checkRepository = new CheckRepository()
2425

2526
const protocolResult = getUrlProtocol(env.RPC_URL)
2627

@@ -90,14 +91,14 @@ export class MonitoringService {
9091
* Initialize monitoring state from database
9192
*/
9293
private async initializeMonitoring(): Promise<void> {
93-
const latestMonitoring = await this.monitoringRepository.findLatestMonitoring()
94+
const latestCheck = await this.checkRepository.findLatestCheck()
9495

95-
if (latestMonitoring.isErr()) {
96-
console.error("Failed to retrieve latest monitoring:", latestMonitoring.error)
96+
if (latestCheck.isErr()) {
97+
console.error("Failed to retrieve latest check:", latestCheck.error)
9798
process.exit(1)
9899
}
99100

100-
this.latestMonitoringBlockNumber = latestMonitoring.value?.blockNumber
101+
this.latestMonitoringBlockNumber = latestCheck.value?.blockNumber
101102
console.log("Starting monitoring from block:", this.latestMonitoringBlockNumber)
102103
}
103104

@@ -109,7 +110,7 @@ export class MonitoringService {
109110
return
110111
}
111112

112-
this.monitoringRepository.pruneMonitoring(block.timestamp)
113+
this.checkRepository.pruneChecks(block.timestamp)
113114

114115
this.latestBlockchainBlockNumber = block.number
115116

@@ -135,19 +136,17 @@ export class MonitoringService {
135136
this.latestMonitoringBlockNumber = this.latestBlockchainBlockNumber
136137
}
137138

138-
if (this.latestMonitoringBlockNumber && this.latestBlockchainBlockNumber) {
139-
let blockToMonitor = this.latestMonitoringBlockNumber + 1n
140-
const endBlock = this.latestBlockchainBlockNumber
139+
let blockToMonitor = this.latestMonitoringBlockNumber + 1n
140+
const endBlock = this.latestBlockchainBlockNumber
141141

142-
while (blockToMonitor < endBlock) {
143-
const result = await this.monitorBlock(blockToMonitor)
144-
if (result.isErr()) {
145-
break
146-
}
147-
148-
this.latestMonitoringBlockNumber = blockToMonitor
149-
blockToMonitor = blockToMonitor + 1n
142+
while (blockToMonitor < endBlock) {
143+
const result = await this.monitorBlock(blockToMonitor)
144+
if (result.isErr()) {
145+
break
150146
}
147+
148+
this.latestMonitoringBlockNumber = blockToMonitor
149+
blockToMonitor = blockToMonitor + 1n
151150
}
152151
} catch (error) {
153152
console.error("Error in processing monitoring:", error)
@@ -160,7 +159,7 @@ export class MonitoringService {
160159
* Monitor a specific block for randomness
161160
*/
162161
private async monitorBlock(blockNumber: bigint): Promise<Result<void, Error>> {
163-
console.log("Monitoring block", blockNumber)
162+
logger.info(`Monitoring block ${blockNumber}`)
164163

165164
let block: Block<bigint, false, "latest"> | undefined
166165
try {
@@ -184,12 +183,12 @@ export class MonitoringService {
184183
functionName: "random",
185184
blockNumber,
186185
})
187-
await this.monitoringRepository
188-
.saveMonitoring(
189-
new Monitoring(blockNumber, block.timestamp, MonitoringResult.Success, undefined, random),
186+
await this.checkRepository
187+
.saveCheck(
188+
new Check(blockNumber, block.timestamp, CheckResult.Success, undefined, random),
190189
)
191190
.catch((error) => {
192-
console.error("Error in saving monitoring:", error)
191+
console.error("Error in saving check:", error)
193192
})
194193
} catch (err: unknown) {
195194
let errorMessage: string | undefined
@@ -200,10 +199,10 @@ export class MonitoringService {
200199
errorMessage = JSON.stringify(err, bigIntReplacer)
201200
}
202201

203-
await this.monitoringRepository
204-
.saveMonitoring(new Monitoring(blockNumber, block.timestamp, MonitoringResult.Failure, errorMessage))
202+
await this.checkRepository
203+
.saveCheck(new Check(blockNumber, block.timestamp, CheckResult.Failure, errorMessage))
205204
.catch((error) => {
206-
console.error("Error in saving monitoring:", error)
205+
console.error("Error in saving check:", error)
207206
})
208207
}
209208

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import { Logger } from "@happy.tech/common"
2+
export const logger = Logger.create("RandomnessMonitor")

0 commit comments

Comments
 (0)