Skip to content

Commit 50f1ed4

Browse files
GabrielMartinezRodrigueznorswap
authored andcommitted
chore(randomness): pr review
1 parent f24da8e commit 50f1ed4

16 files changed

Lines changed: 102 additions & 104 deletions

File tree

apps/randomness/src/Drand.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ export class Drand {
1313
#status: DrandStatus
1414
#transactionIntentId: UUID | undefined
1515

16+
static readonly validTransitions: Record<DrandStatus, DrandStatus[]> = {
17+
PENDING: [DrandStatus.SUBMITTED, DrandStatus.FAILED],
18+
SUBMITTED: [DrandStatus.SUCCESS, DrandStatus.FAILED],
19+
SUCCESS: [],
20+
FAILED: [],
21+
}
22+
1623
constructor(params: {
1724
status: DrandStatus
1825
transactionIntentId?: UUID
@@ -26,49 +33,42 @@ export class Drand {
2633
}
2734

2835
private setStatus(newStatus: DrandStatus): void {
29-
const validTransitions: Record<DrandStatus, DrandStatus[]> = {
30-
PENDING: [DrandStatus.SUBMITTED, DrandStatus.FAILED],
31-
SUBMITTED: [DrandStatus.SUCCESS, DrandStatus.FAILED],
32-
SUCCESS: [],
33-
FAILED: [],
34-
}
35-
36-
if (!validTransitions[this.#status].includes(newStatus)) {
36+
if (!Drand.validTransitions[this.#status].includes(newStatus)) {
3737
throw new Error(`Invalid status transition from ${this.#status} to ${newStatus}`)
3838
}
3939

4040
this.#status = newStatus
4141
}
4242

43-
public executionSuccess(): void {
43+
executionSuccess(): void {
4444
this.setStatus(DrandStatus.SUCCESS)
4545
}
4646

47-
public transactionSubmitted(): void {
47+
transactionSubmitted(): void {
4848
this.setStatus(DrandStatus.SUBMITTED)
4949
}
5050

51-
public transactionFailed(): void {
51+
transactionFailed(): void {
5252
this.setStatus(DrandStatus.FAILED)
5353
}
5454

55-
public get round(): bigint {
55+
get round(): bigint {
5656
return this.#round
5757
}
5858

59-
public get signature(): Hex | undefined {
59+
get signature(): Hex | undefined {
6060
return this.#signature
6161
}
6262

63-
public get status(): DrandStatus {
63+
get status(): DrandStatus {
6464
return this.#status
6565
}
6666

67-
public get transactionIntentId(): UUID | undefined {
67+
get transactionIntentId(): UUID | undefined {
6868
return this.#transactionIntentId
6969
}
7070

71-
static createDrand(params: {
71+
static create(params: {
7272
transactionIntentId?: UUID
7373
round: bigint
7474
signature: Hex

apps/randomness/src/DrandRepository.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import { type Hex, type UUID, bigIntToZeroPadded, unknownToError } from "@happy.tech/common"
22
import { type Result, ResultAsync } from "neverthrow"
3-
import { Drand, DrandStatus } from "./Drand"
3+
import { Drand } from "./Drand"
4+
import { DIGITS_MAX_UINT256 } from "./constants"
45
import { db } from "./db/driver"
56
import type { DrandRow } from "./db/types"
67

7-
// Quantity of digits in the max uint256 value
8-
export const DIGITS_MAX_UINT256 = 78
9-
108
export class DrandRepository {
119
private cache: Drand[] = []
1210

@@ -28,13 +26,13 @@ export class DrandRepository {
2826
}
2927
}
3028

31-
public async start(): Promise<void> {
29+
async start(): Promise<void> {
3230
const drandsDb = (await db.selectFrom("drands").selectAll().execute()).map(this.rowToEntity)
3331

3432
this.cache.push(...drandsDb)
3533
}
3634

37-
public async saveDrand(drand: Drand): Promise<Result<void, Error>> {
35+
async saveDrand(drand: Drand): Promise<Result<void, Error>> {
3836
const row = this.entityToRow(drand)
3937

4038
const result = await ResultAsync.fromPromise(db.insertInto("drands").values(row).execute(), unknownToError)
@@ -46,14 +44,14 @@ export class DrandRepository {
4644
return result.map(() => undefined)
4745
}
4846

49-
public getOldestDrandRound(): bigint | undefined {
47+
getOldestDrandRound(): bigint | undefined {
5048
if (this.cache.length === 0) {
5149
return undefined
5250
}
5351
return this.cache.reduce((acc, drand) => (drand.round < acc ? drand.round : acc), this.cache[0].round)
5452
}
5553

56-
public findRoundGapsInRange(startRound: bigint, endRound: bigint): bigint[] {
54+
findRoundGapsInRange(startRound: bigint, endRound: bigint): bigint[] {
5755
const roundGaps = []
5856
for (let round = startRound; round <= endRound; round++) {
5957
if (!this.cache.find((drand) => drand.round === round)) {
@@ -63,19 +61,15 @@ export class DrandRepository {
6361
return roundGaps
6462
}
6563

66-
public getDrand(round: bigint): Drand | undefined {
64+
getDrand(round: bigint): Drand | undefined {
6765
return this.cache.find((drand) => drand.round === round)
6866
}
6967

70-
public getDrandByTransactionIntentId(transactionIntentId: UUID): Drand | undefined {
68+
getDrandByTransactionIntentId(transactionIntentId: UUID): Drand | undefined {
7169
return this.cache.find((drand) => drand.transactionIntentId === transactionIntentId)
7270
}
7371

74-
public getPendingDrands(): Drand[] {
75-
return this.cache.filter((drand) => drand.status === DrandStatus.PENDING)
76-
}
77-
78-
public async updateDrand(drand: Drand): Promise<Result<void, Error>> {
72+
async updateDrand(drand: Drand): Promise<Result<void, Error>> {
7973
const row = this.entityToRow(drand)
8074
return ResultAsync.fromPromise(
8175
db

apps/randomness/src/DrandService.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { Hex } from "viem"
44
import { z } from "zod"
55
import { env } from "./env"
66

7-
export const drandBeaconSchema = z.object({
7+
const drandBeaconSchema = z.object({
88
round: z.number().int().positive(),
99
signature: z
1010
.string()
@@ -32,7 +32,7 @@ export enum DrandError {
3232
}
3333

3434
export class DrandService {
35-
public async getDrandBeacon(round: bigint): Promise<Result<DrandBeacon, DrandError>> {
35+
async getDrandBeacon(round: bigint): Promise<Result<DrandBeacon, DrandError>> {
3636
if (round <= 0n) {
3737
return err(DrandError.InvalidRound)
3838
}
@@ -55,19 +55,16 @@ export class DrandService {
5555

5656
const dataRaw = await response.value.json()
5757

58-
console.log(dataRaw)
5958
const parsed = drandBeaconSchema.safeParse(dataRaw)
6059

6160
if (!parsed.success) {
6261
return err(DrandError.InvalidResponse)
6362
}
6463

65-
const data = parsed.data
66-
67-
return ok(data)
64+
return ok(parsed.data)
6865
}
6966

70-
public currentRound(): bigint {
67+
currentRound(): bigint {
7168
const currentTimestamp = nowInSeconds()
7269
const currentRound = Math.floor(
7370
(currentTimestamp - Number(env.EVM_DRAND_GENESIS_TIMESTAMP_SECONDS)) /

apps/randomness/src/Randomness.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,6 @@ export enum RandomnessStatus {
1313
REVEAL_NOT_SUBMITTED_ON_TIME = "REVEAL_NOT_SUBMITTED_ON_TIME",
1414
}
1515

16-
export const FINALIZED_STATUSES = [
17-
RandomnessStatus.REVEAL_EXECUTED,
18-
RandomnessStatus.REVEAL_FAILED,
19-
RandomnessStatus.REVEAL_NOT_SUBMITTED_ON_TIME,
20-
]
21-
2216
export class Randomness {
2317
public blockNumber: bigint
2418
public value: bigint
@@ -43,33 +37,33 @@ export class Randomness {
4337
this.status = params.status
4438
}
4539

46-
public commitmentExecuted(): void {
40+
commitmentExecuted(): void {
4741
this.status = RandomnessStatus.COMMITMENT_EXECUTED
4842
}
4943

50-
public revealExecuted(): void {
44+
revealExecuted(): void {
5145
this.status = RandomnessStatus.REVEAL_EXECUTED
5246
}
5347

54-
public addCommitmentTransactionIntentId(intentId: UUID): void {
48+
addCommitmentTransactionIntentId(intentId: UUID): void {
5549
this.commitmentTransactionIntentId = intentId
5650
this.status = RandomnessStatus.COMMITMENT_SUBMITTED
5751
}
5852

59-
public addRevealTransactionIntentId(intentId: UUID): void {
53+
addRevealTransactionIntentId(intentId: UUID): void {
6054
this.revealTransactionIntentId = intentId
6155
this.status = RandomnessStatus.REVEAL_SUBMITTED
6256
}
6357

64-
public commitmentFailed(): void {
58+
commitmentFailed(): void {
6559
this.status = RandomnessStatus.COMMITMENT_FAILED
6660
}
6761

68-
public revealFailed(): void {
62+
revealFailed(): void {
6963
this.status = RandomnessStatus.REVEAL_FAILED
7064
}
7165

72-
public revealNotSubmittedOnTime(): void {
66+
revealNotSubmittedOnTime(): void {
7367
this.status = RandomnessStatus.REVEAL_NOT_SUBMITTED_ON_TIME
7468
}
7569

apps/randomness/src/RandomnessRepository.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,52 @@
11
import { type UUID, unknownToError } from "@happy.tech/common"
22
import { bigIntToZeroPadded } from "@happy.tech/common"
33
import { type Result, ResultAsync } from "neverthrow"
4-
import { FINALIZED_STATUSES, type Randomness, type RandomnessStatus } from "./Randomness"
4+
import { type Randomness, RandomnessStatus } from "./Randomness"
5+
import { DIGITS_MAX_UINT256 } from "./constants"
56
import { db } from "./db/driver"
67
import { randomnessEntityToRow, randomnessRowToEntity } from "./db/types"
78

8-
const COMMITMENT_PRUNE_INTERVAL_BLOCKS = 120n // 2 minutes
9+
const COMMITMENT_PRUNE_INTERVAL_BLOCKS = 60n // 2 minutes
910

10-
// Quantity of digits in the max uint256 value
11-
export const DIGITS_MAX_UINT256 = 78
11+
export const FINALIZED_STATUSES = [
12+
RandomnessStatus.REVEAL_EXECUTED,
13+
RandomnessStatus.REVEAL_FAILED,
14+
RandomnessStatus.REVEAL_NOT_SUBMITTED_ON_TIME,
15+
]
1216

1317
export class RandomnessRepository {
14-
private readonly map = new Map<bigint, Randomness>()
18+
// In-memory cache that maps block numbers to their corresponding Randomness
19+
private readonly cache = new Map<bigint, Randomness>()
1520

1621
async start(): Promise<void> {
1722
const randomnessesDb = (await db.selectFrom("randomnesses").selectAll().execute()).map(randomnessRowToEntity)
1823
for (const randomness of randomnessesDb) {
19-
this.map.set(randomness.blockNumber, randomness)
24+
this.cache.set(randomness.blockNumber, randomness)
2025
}
2126
}
2227

2328
getRandomnessForBlockNumber(blockNumber: bigint): Randomness | undefined {
24-
return this.map.get(blockNumber)
29+
return this.cache.get(blockNumber)
2530
}
2631

2732
getRandomnessForIntentId(intentId: UUID): Randomness | undefined {
28-
return Array.from(this.map.values()).find(
33+
return Array.from(this.cache.values()).find(
2934
(randomness) =>
3035
randomness.commitmentTransactionIntentId === intentId ||
3136
randomness.revealTransactionIntentId === intentId,
3237
)
3338
}
3439

35-
getRandomnessInBlockRange(start: bigint, end: bigint): Randomness[] {
36-
return Array.from(this.map.values()).filter(
37-
(randomness) => randomness.blockNumber >= start && randomness.blockNumber <= end,
38-
)
39-
}
40-
4140
getRandomnessInStatus(status: RandomnessStatus): Randomness[] {
42-
return Array.from(this.map.values()).filter((randomness) => randomness.status === status)
41+
return Array.from(this.cache.values()).filter((randomness) => randomness.status === status)
4342
}
4443

4544
/**
4645
* Save a randomness to the database
4746
* Even if the operation fails, the randomness will be saved in a in-memory cache
4847
*/
4948
async saveRandomness(randomness: Randomness): Promise<Result<void, Error>> {
50-
this.map.set(randomness.blockNumber, randomness)
49+
this.cache.set(randomness.blockNumber, randomness)
5150
const row = randomnessEntityToRow(randomness)
5251
return await ResultAsync.fromPromise(db.insertInto("randomnesses").values(row).execute(), unknownToError).map(
5352
() => undefined,
@@ -59,7 +58,7 @@ export class RandomnessRepository {
5958
* Even if the operation fails, the randomness will be updated in a in-memory cache
6059
*/
6160
async updateRandomness(randomness: Randomness): Promise<Result<void, Error>> {
62-
this.map.set(randomness.blockNumber, randomness)
61+
this.cache.set(randomness.blockNumber, randomness)
6362
const row = randomnessEntityToRow(randomness)
6463
return await ResultAsync.fromPromise(
6564
db
@@ -73,9 +72,9 @@ export class RandomnessRepository {
7372

7473
async pruneRandomnesses(latestBlock: bigint): Promise<Result<void, Error>> {
7574
const cutoffBlock = latestBlock - COMMITMENT_PRUNE_INTERVAL_BLOCKS
76-
for (const blockNumber of this.map.keys()) {
75+
for (const blockNumber of this.cache.keys()) {
7776
if (blockNumber < cutoffBlock) {
78-
this.map.delete(blockNumber)
77+
this.cache.delete(blockNumber)
7978
}
8079
}
8180

apps/randomness/src/TransactionFactory.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export class TransactionFactory {
1313
) {}
1414

1515
createCommitmentTransaction(randomness: Randomness): Transaction {
16+
// The commitment transaction must be submitted at least one block
17+
// before the deadline, because submissions after the precommit delay
18+
// are not allowed. We compute the deadline by subtracting the precommit
19+
// delay from the block number, multiplying by the block time, and adding
20+
// the genesis timestamp.
1621
const deadline = Number(
1722
(randomness.blockNumber - this.precommitDelay) * env.BLOCK_TIME + env.HAPPY_GENESIS_TIMESTAMP_SECONDS,
1823
)
@@ -26,6 +31,9 @@ export class TransactionFactory {
2631
}
2732

2833
createRevealValueTransaction(randomness: Randomness): Transaction {
34+
// The reveal transaction must execute in its target block.
35+
// We calculate its deadline by multiplying the block number by the block time
36+
// and adding the genesis timestamp.
2937
const deadline = Number(randomness.blockNumber * env.BLOCK_TIME + env.HAPPY_GENESIS_TIMESTAMP_SECONDS)
3038

3139
return this.transactionManager.createTransaction({
@@ -37,7 +45,7 @@ export class TransactionFactory {
3745
})
3846
}
3947

40-
public createPostDrandTransaction({
48+
createPostDrandTransaction({
4149
round,
4250
signature,
4351
}: {

apps/randomness/src/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Quantity of digits in the max uint256 value
2+
export const DIGITS_MAX_UINT256 = 78

apps/randomness/src/db/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { type Hex, type UUID, bigIntToZeroPadded } from "@happy.tech/common"
22
import type { DrandStatus } from "../Drand"
33
import { Randomness, type RandomnessStatus } from "../Randomness"
4-
import { DIGITS_MAX_UINT256 } from "../RandomnessRepository"
4+
import { DIGITS_MAX_UINT256 } from "../constants"
55

66
// Values are stored as strings because they can be large numbers bigger than the max value of an SQLite integer
77
export interface RandomnessRow {

0 commit comments

Comments
 (0)