Skip to content

Commit

Permalink
Merge pull request #53 from ATOR-Development/52-set-uptimes-in-distri…
Browse files Browse the repository at this point in the history
…bution-contract

Pushes relay uptimes to distribution contract after populating them
  • Loading branch information
jim-toth authored Aug 10, 2024
2 parents 9496a5c + 3bb8383 commit 269502e
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ export class PopulateRelayUptimeJob {

@Prop({ type: Number, required: true })
uptimeMinimumRunningCount: number

@Prop({
type: [{ fingerprint: String, uptime_days: Number, pushed: Boolean }],
required: true,
default: []
})
uptimes: { fingerprint: String, uptime_days: Number, pushed: Boolean }[]
}

export type RelayUptimeJobDocument = HydratedDocument<PopulateRelayUptimeJob>
Expand Down
149 changes: 110 additions & 39 deletions cli/commands/populate-relay-uptime/populate-relay-uptime.command.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Logger } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import _ from 'lodash'
import { Model } from 'mongoose'
import { Document, Model, Types } from 'mongoose'
import { Command, CommandRunner } from 'nest-commander'

import { PopulateRelayUptimeJob } from './populate-relay-uptime-job'
Expand All @@ -10,13 +10,20 @@ import {
UptimeValidationService
} from '../../../src/validation/uptime-validation.service'
import { RelayData } from '../../../src/validation/schemas/relay-data'
import {
DistributionService
} from '../../../src/distribution/distribution.service'
import { RelayUptime } from 'src/validation/schemas/relay-uptime'

@Command({ name: 'populate-relay-uptime' })
export class PopulateRelayUptimeCommand extends CommandRunner {
static setUptimesBatchSize = 12

private readonly logger = new Logger(PopulateRelayUptimeCommand.name)

constructor(
private readonly uptimeValidationService: UptimeValidationService,
private readonly distributionService: DistributionService,
@InjectModel(PopulateRelayUptimeJob.name)
private readonly populateRelayUptimeJobModel: Model<PopulateRelayUptimeJob>,
@InjectModel(RelayData.name)
Expand Down Expand Up @@ -55,21 +62,9 @@ export class PopulateRelayUptimeCommand extends CommandRunner {
return extractIsodate(startDate.setDate(startDate.getDate() + 1))
}

async run() {
this.logger.log('Starting populate relay uptime job')
const startDate = await this.determineStartDate()

if (!startDate) {
this.logger.log(
'Could not determine start date, likely due to no RelayData'
)
return
}

private determineValidationDates(startDate: string) {
const now = new Date()
const endDateTimestamp = now.setDate(now.getDate() - 1)
const endDate = extractIsodate(endDateTimestamp)

const validation_dates: string[] = []
let currentTimestamp = new Date(startDate).getTime()
while (currentTimestamp <= endDateTimestamp) {
Expand All @@ -79,9 +74,23 @@ export class PopulateRelayUptimeCommand extends CommandRunner {
currentTimestamp = d.setDate(d.getDate() + 1)
}

this.logger.log(
`Found dates needing relay uptime calcs: ${validation_dates.toString()}`
)
return validation_dates
}

private async runPopulateRelayUptimeJobsAndReturnLatest(
validation_dates: string[]
): Promise<
(
Document<unknown, {}, PopulateRelayUptimeJob>
& PopulateRelayUptimeJob
& { _id: Types.ObjectId }
) | null
> {
let latestJob: (
Document<unknown, {}, PopulateRelayUptimeJob>
& PopulateRelayUptimeJob
& { _id: Types.ObjectId }
) | null = null

for (const validation_date of validation_dates) {
this.logger.log(`Populating relay uptime data for ${validation_date}`)
Expand All @@ -91,30 +100,92 @@ export class PopulateRelayUptimeCommand extends CommandRunner {
})

if (existingJobRun) {
this.logger.log(`Already ran job for ${validation_date}, skipping`)

continue
this.logger.log(`Found successful job run for ${validation_date}`)
latestJob = existingJobRun
} else {
this.logger.log(`No job run found for ${validation_date}, creating it`)
const createdJob = await this.populateRelayUptimeJobModel
.create<PopulateRelayUptimeJob>({
validation_date,
uptimeMinimumRunningCount:
this.uptimeValidationService.uptimeMinimumRunningCount,
uptimes: []
})

try {
const uptimes = await this
.uptimeValidationService
.populateRelayUptimesForDate(validation_date)
createdJob.uptimes = uptimes.map(
({ fingerprint, uptime_days }) =>
({ fingerprint, uptime_days, pushed: false })
)
createdJob.success = true
} catch (error) {
createdJob.success = false
this.logger.log(`Job failed due to error`, error.stack)
}

createdJob.finishedAt = Date.now()
await createdJob.save()
latestJob = createdJob
}

const jobRun = await this.populateRelayUptimeJobModel
.create<PopulateRelayUptimeJob>({
validation_date,
uptimeMinimumRunningCount:
this.uptimeValidationService.uptimeMinimumRunningCount
})

try {
await this.uptimeValidationService.populateRelayUptimesForDate(
validation_date
)
jobRun.success = true
} catch (error) {
jobRun.success = false
this.logger.log(`Job failed due to error`, error.stack)
}

return latestJob
}

async run() {
this.logger.log('Starting populate relay uptime job')
const startDate = await this.determineStartDate()

if (!startDate) {
this.logger.log(
'Could not determine start date, likely due to no RelayData'
)
return
}

const validation_dates = this.determineValidationDates(startDate)

this.logger.log(
`Found dates needing relay uptime calcs: ${validation_dates.toString()}`
)

const latestJobRun = await this.runPopulateRelayUptimeJobsAndReturnLatest(
validation_dates
)

if (!latestJobRun) {
this.logger.log('Did not receive latest job run, exiting')
return
}

const uptimesToPush = latestJobRun.uptimes.filter(u => !u.pushed)
if (uptimesToPush.length < 1) {
this.logger.log(
`No uptimes to push to distribution contract for ${latestJobRun.validation_date}`
)
return
}

const batches = _.chunk(
uptimesToPush,
PopulateRelayUptimeCommand.setUptimesBatchSize
)
for (const batch of batches) {
const { success } = await this.distributionService.setRelayUptimes(batch)
const successfulFingerprints = batch.map(u => u.fingerprint)
if (success) {
for (let i = 0; i < latestJobRun.uptimes.length; i++) {
if (
successfulFingerprints.includes(latestJobRun.uptimes[i].fingerprint)
) {
latestJobRun.uptimes[i].pushed = true
}
}
await latestJobRun.save()
}

jobRun.finishedAt = Date.now()
await jobRun.save()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ import {
RelayUptime,
RelayUptimeSchema
} from '../../../src/validation/schemas/relay-uptime'
import {
DistributionModule
} from '../../../src/distribution/distribution.module'

@Module({
imports: [
ConfigModule,
DistributionModule,
MongooseModule.forFeature([
{ name: PopulateRelayUptimeJob.name, schema: RelayUptimeJobSchema },
{ name: RelayData.name, schema: RelayDataSchema },
Expand Down
62 changes: 58 additions & 4 deletions src/distribution/distribution.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
DistributionState,
Score,
SetFamilies,
SetQualityTierUptimes,
} from 'src/distribution/interfaces/distribution'
import { ConfigService } from '@nestjs/config'
import { Wallet, ethers } from 'ethers'
import { EthersExtension } from 'warp-contracts-plugin-ethers'
import {
EthereumSigner,
Expand All @@ -28,14 +28,18 @@ import Bundlr from '@bundlr-network/client'
import { Distribute } from './interfaces/distribution'
import { RewardAllocationData } from './dto/reward-allocation-data'
import { Claimable } from 'src/verification/interfaces/relay-registry'
import { DistributionCompletedResults } from './dto/distribution-completed-result'
import { setTimeout } from 'node:timers/promises'
import { AxiosError } from 'axios'
import { DreDistributionResponse } from './interfaces/dre-relay-registry-response'
import {
DreDistributionResponse
} from './interfaces/dre-relay-registry-response'
import { HttpService } from '@nestjs/axios'
import { ValidatedRelay } from 'src/validation/schemas/validated-relay'
import { VerificationResults } from 'src/verification/dto/verification-result-dto'
import {
VerificationResults
} from 'src/verification/dto/verification-result-dto'
import _ from 'lodash'
import { RelayUptime } from 'src/validation/schemas/relay-uptime'

@Injectable()
export class DistributionService {
Expand Down Expand Up @@ -649,4 +653,54 @@ export class DistributionService {

return results.concat(relays.map(relay => ({ relay, result: 'OK' })))
}

public async setRelayUptimes(
uptimes: { fingerprint: String, uptime_days: Number, pushed: Boolean }[]
): Promise<{ success: boolean }> {
if (!this.distributionContract) {
this.logger.error('Distribution contract not initialized')
return { success: false }
}

if (!this.operator) {
this.logger.error('Distribution operator not defined')
return { success: false }
}

if (this.isLive !== 'true') {
this.logger.warn(`NOT LIVE - skipped setting relay uptimes`)
return { success: true }
}

await setTimeout(5000)
const fingerprints = uptimes.map(r => r.fingerprint)
this.logger.log(
`Setting uptimes for ${uptimes.length} relays [${fingerprints}]`
)

try {
const response = await this.distributionContract
.writeInteraction<SetQualityTierUptimes>({
function: 'setQualityTierUptimes',
uptimes: Object.fromEntries(
uptimes.map(
({ fingerprint, uptime_days }) =>
[ fingerprint, uptime_days ]
)
)
})
this.logger.log(
`Set uptimes for ${uptimes.length} relays: ${response?.originalTxId}`
)
} catch (error) {
this.logger.error(
`Exception setting uptimes for ${uptimes.length} relays [${fingerprints}]`,
error.stack
)

return { success: false }
}

return { success: true }
}
}
Loading

0 comments on commit 269502e

Please sign in to comment.