Skip to content

Commit

Permalink
Merge pull request #233 from gobitfly/BIDS-2306/harmonizeLuck
Browse files Browse the repository at this point in the history
(BIDS-2306) permanently harmonize proposal luck
  • Loading branch information
LuccaBitfly authored Sep 4, 2023
2 parents 380c92d + c9cbcae commit a736199
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 172 deletions.
8 changes: 8 additions & 0 deletions src/app/requests/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ export interface DashboardResponse {
current_sync_committee: SyncCommitteeResponse[]
next_sync_committee: SyncCommitteeResponse[]
sync_committees_stats: SyncCommitteesStatisticsResponse
proposal_luck_stats: ProposalLuckResponse
}

export interface RocketPoolNetworkStats {
Expand Down Expand Up @@ -320,6 +321,13 @@ export interface BitflyAdResponse {
height: string
}

export interface ProposalLuckResponse {
proposal_luck: number
average_proposal_interval: number
next_proposal_estimate_ts: number
time_frame_name: string
}

// ------------- Reqests -------------

export class DashboardRequest extends APIRequest<DashboardResponse> {
Expand Down
11 changes: 6 additions & 5 deletions src/app/tab-blocks/tab-blocks.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

<ion-item class="top-bar" [class]="fadeIn">
<ion-label
>Proposal Luck: <span *ngIf="luck" [class]="luck.luckPercentage | valuestyle: 1">{{ luck.luckPercentage*100 | number:'1.0-1' }}%</span>
>Proposal Luck:
<span *ngIf="luck && luck.luckPercentage" [class]="luck.luckPercentage | valuestyle: 1">{{ luck.luckPercentage*100 | number:'1.0-1' }}%</span>
<span *ngIf="!luck">-</span>
</ion-label>

Expand All @@ -23,11 +24,11 @@
<ion-refresher-content></ion-refresher-content>
</ion-refresher>

<p class="next-block-hint" *ngIf="nextBlockEstimate">
<span *ngIf="!(nextBlockEstimate | timeago).includes('ago')">On average next block expected in</span>
<span *ngIf="(nextBlockEstimate| timeago).includes('ago')">On average next block was expected</span>
<p class="next-block-hint" *ngIf="luck && luck.nextBlockEstimate">
<span *ngIf="!(luck.nextBlockEstimate | timeago).includes('ago')">On average next block expected in</span>
<span *ngIf="(luck.nextBlockEstimate| timeago).includes('ago')">On average next block was expected</span>
<br />
<span style="font-style: normal">~{{ nextBlockEstimate | timeago }} </span>
<span style="font-style: normal">~{{ luck.nextBlockEstimate | timeago }} </span>
</p>

<app-ad location="blocks" *ngIf="items && items.length > 0 && !loading"></app-ad>
Expand Down
17 changes: 7 additions & 10 deletions src/app/tab-blocks/tab-blocks.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class TabBlocksPage implements OnInit {

luck: Luck = null

nextBlockEstimate = null
valis = null

constructor(
public api: ApiService,
Expand Down Expand Up @@ -72,15 +72,15 @@ export class TabBlocksPage implements OnInit {
const blocks = await this.blockUtils.getMyBlocks(initial ? 0 : this.items.length)
if (initial) {
this.items = blocks
this.luck = await this.blockUtils.getProposalLuck()
this.valis = await this.validatorUtils.getAllValidatorsLocal()
this.initialized = true
} else {
this.items = this.items.concat(blocks)
}
this.luck = await this.blockUtils.getProposalLuck(this.items)
this.nextBlockEstimate = await this.blockUtils.getNextBlockEstimate(this.items)

TabBlocksPage.itemCount = this.items.length
if (blocks.length < 25) {
if (blocks.length < this.blockUtils.getLimit(this.valis.length)) {
this.reachedMax = true
}
}
Expand Down Expand Up @@ -146,13 +146,10 @@ export class TabBlocksPage implements OnInit {
this.alertService.showInfo(
'Proposal Luck',
`Compares the number of your actual proposed blocks to the expected average blocks per validator during the last <strong>${
this.luck.timeFrameName
this.luck.timeFrameName ? this.luck.timeFrameName : 'month'
}</strong>.
<br/><br/>Your <strong>${
this.luck.userValidators
}</strong> validators are expected to produce <strong>${this.luck.expectedBlocksPerMonth.toFixed(
2
)}</strong> blocks per month on average with current network conditions.`
<br/><br/>Your ${this.luck.userValidators == 1 ? `validator is` : `<strong>${this.luck.userValidators}</strong> validators are`}
expected to produce <strong>${this.luck.expectedBlocksPerMonth.toFixed(2)}</strong> blocks per month on average with current network conditions.`
)
}
}
Expand Down
8 changes: 2 additions & 6 deletions src/app/tab-dashboard/tab-dashboard.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,8 @@ <h2 [ngStyle]="{'opacity': currentY | fadeoutpipe: 180 }">Let´s get started</h2
<ion-refresher-content></ion-refresher-content>
</ion-refresher>

<app-validator-dashboard
[data]="overallData"
[currentY]="currentY"
[scrolling]="scrolling"
[updates]="updates.updates"
*ngIf="initialized"></app-validator-dashboard>
<app-validator-dashboard [data]="overallData" [currentY]="currentY" [scrolling]="scrolling" [updates]="updates.updates" *ngIf="initialized">
</app-validator-dashboard>

<app-help *ngIf="!initialized"></app-help>
</ion-content>
160 changes: 16 additions & 144 deletions src/app/utils/BlockUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,15 @@

import { ApiService } from '../services/api.service'
import { Injectable } from '@angular/core'
import { BlockProducedByRequest, BlockResponse } from '../requests/requests'
import { BlockProducedByRequest, BlockResponse, DashboardRequest } from '../requests/requests'
import { CacheModule } from './CacheModule'
import BigNumber from 'bignumber.js'
import { ValidatorUtils } from './ValidatorUtils'

export const ROCKETPOOL_SMOOTHING_POOL = '0xd4e96ef8eee8678dbff4d535e033ed1a4f7605b7'
export const ETHPOOL = '0xb364e75b1189dcbbf7f0c856456c1ba8e4d6481b'

const YEAR = 1000 * 60 * 60 * 24 * 365
const SIXMONTH = 1000 * 60 * 60 * 24 * 180
const FIVEMONTH = 1000 * 60 * 60 * 24 * 150
const FOURMONTH = 1000 * 60 * 60 * 24 * 120
const THREEMONTH = 1000 * 60 * 60 * 24 * 90
const TWOEMONTH = 1000 * 60 * 60 * 24 * 60
const SIXWEEKS = 1000 * 60 * 60 * 24 * 45
const MONTH = 1000 * 60 * 60 * 24 * 30
const WEEK = 1000 * 60 * 60 * 24 * 7
const FIVEDAYS = 1000 * 60 * 60 * 24 * 5
const MONTH = 60 * 60 * 24 * 30

@Injectable({
providedIn: 'root',
Expand All @@ -62,16 +53,16 @@ export class BlockUtils extends CacheModule {
}

async getMyBlocks(offset: number): Promise<BlockResponse[]> {
const valis = await this.validatorUtils.getLocalValidatorIndexes()
const valis = await this.validatorUtils.getAllValidatorsLocal()
if (valis.length == 0) return []

const request = new BlockProducedByRequest(offset, this.getLimit(valis.length), valis)
const request = new BlockProducedByRequest(offset, this.getLimit(valis.length), ...valis.map((vali) => vali.index))
const response = await this.api.execute(request)
const result = request.parse(response)
return result
}

private getLimit(validatorCount: number): number {
public getLimit(validatorCount: number): number {
if (validatorCount <= 4) return 10
else if (validatorCount <= 10) return 15
else if (validatorCount <= 20) return 20
Expand All @@ -81,147 +72,28 @@ export class BlockUtils extends CacheModule {
else return 75
}

async getProposalLuck(blocks: BlockResponse[]): Promise<Luck> {
if (blocks.length <= 0) return null
const earliestBlock = blocks[blocks.length - 1]

async getProposalLuck(): Promise<Luck> {
const valis = await this.validatorUtils.getAllValidatorsLocal()
if (valis.length <= 0) return null
const currentEpoch = await this.validatorUtils.getRemoteCurrentEpoch()

// calculate blocks with 30d timeframe to see how many blocks we would get
const blocksIn30d = this.calculateExpectedBlocksInTimeframe(MONTH, currentEpoch.validatorscount, valis.length)

// var timeframe = this.getTimeframe(earliestBlock.timestamp * 1000, blocksIn30d)
const timeframe = this.findTimeFrameNew(earliestBlock.timestamp * 1000, blocksIn30d)

if (timeframe == -1) return null

const blocksAfterTimeframe = this.getBlocksAfterTs(blocks, timeframe)
if (blocksAfterTimeframe.length <= 0) return null

const avgBlockInTimeframe = this.calculateExpectedBlocksInTimeframe(timeframe, currentEpoch.validatorscount, valis.length)
if (valis.length == 0) return null

const request = new DashboardRequest(...valis.map((vali) => vali.index))
const response = await this.api.execute(request)
const result = request.parse(response)[0]
const proposalLuckStats = result.proposal_luck_stats
return {
luckPercentage: blocksAfterTimeframe.length / avgBlockInTimeframe,
timeFrameName: this.getProposalLuckTimeframeName(timeframe),
expectedBlocksPerMonth: blocksIn30d,
luckPercentage: proposalLuckStats.proposal_luck,
timeFrameName: proposalLuckStats.time_frame_name,
userValidators: valis.length,
proposedBlocksInTimeframe: blocksAfterTimeframe.length,
expectedBlocksPerMonth: MONTH / (proposalLuckStats.average_proposal_interval * 12),
nextBlockEstimate: proposalLuckStats.next_proposal_estimate_ts * 1000,
} as Luck
}

async getNextBlockEstimate(blocks: BlockResponse[]): Promise<number> {
if (blocks.length <= 0) return null

const valis = await this.validatorUtils.getAllValidatorsLocal()
if (valis.length <= 0) return null
const currentEpoch = await this.validatorUtils.getRemoteCurrentEpoch()

const blocksIn30d = this.calculateExpectedBlocksInTimeframe(MONTH, currentEpoch.validatorscount, valis.length)

const newBlockOnAvgDays = MONTH / blocksIn30d
return blocks[0].timestamp * 1000 + newBlockOnAvgDays
}

private getProposalLuckTimeframeName(timeframe: number): string {
switch (timeframe) {
case FIVEMONTH:
return '5 months'
case FOURMONTH:
return '4 months'
case THREEMONTH:
return '3 months'
case TWOEMONTH:
return '2 months'
case SIXWEEKS:
return '6 weeks'
case MONTH:
return 'month'
case WEEK:
return 'week'
case FIVEDAYS:
return '5 days'
case SIXMONTH:
return '6 months'
case YEAR:
return '1 year'
}
return 'month'
}

private calculateExpectedBlocksInTimeframe(ts: number, validatorTotalCount: number, userValidatorCount: number): number {
const slotsInTimeframe = ts / 1000 / 12
return (slotsInTimeframe / validatorTotalCount) * userValidatorCount
}

private getBlocksAfterTs(blocks: BlockResponse[], after: number): BlockResponse[] {
const result = []
const curMilies = Date.now()
for (const block of blocks) {
if (block.timestamp * 1000 > curMilies - after) {
result.push(block)
}
}
return result
}

private findTimeFrameNew(earliestBlockTs: number, blocksPer30d: number) {
const curMilies = Date.now()
const diff = curMilies - earliestBlockTs
const targetBlocks = 8 // target blocks per month

if (diff < FIVEDAYS) {
return FIVEDAYS
} else if (diff < WEEK) {
return WEEK
} else if (diff < MONTH) {
return MONTH
} else if (diff > YEAR && blocksPer30d <= targetBlocks / 12) {
return YEAR
} else if (diff > SIXMONTH && blocksPer30d <= targetBlocks / 6) {
return SIXMONTH
} else if (diff > FIVEMONTH && blocksPer30d <= targetBlocks / 5) {
return FIVEMONTH
} else if (diff > FOURMONTH && blocksPer30d <= targetBlocks / 4) {
return FOURMONTH
} else if (diff > THREEMONTH && blocksPer30d <= targetBlocks / 3) {
return THREEMONTH
} else if (diff > TWOEMONTH && blocksPer30d <= targetBlocks / 2) {
return TWOEMONTH
} else if (diff > SIXWEEKS && blocksPer30d <= targetBlocks / 1.5) {
return SIXWEEKS
}

return MONTH
}

/*private getTimeframe(earliestBlockTs: number, blocksPer30d: number) : number {
let curMilies = Date.now()
let diff = curMilies - earliestBlockTs
// extend the timeframe if less than 2.2 blocks are found per month. Target at least 2.2 - 3.2 blocks per month
if (blocksPer30d <= 2.2) {
if (diff > FOURMONTH && blocksPer30d <= 0.55) {
return FOURMONTH
} else if (diff > THREEMONTH && blocksPer30d <= 0.95) {
return THREEMONTH
} else if (diff > TWOEMONTH && blocksPer30d <= 1.6) {
return TWOEMONTH
} else if (diff > SIXWEEKS) {
return SIXWEEKS
}
}
if (diff > MONTH) return MONTH
else if (diff > WEEK) return WEEK
else if (diff > FIVEDAYS) return FIVEDAYS
return -1
}*/
}

export interface Luck {
luckPercentage: number
timeFrameName: string
userValidators: number
expectedBlocksPerMonth: number
proposedBlocksInTimeframe: number
nextBlockEstimate: number
}
7 changes: 0 additions & 7 deletions src/app/utils/ValidatorUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,6 @@ export class ValidatorUtils extends CacheModule {
return null
}

async getLocalValidatorIndexes(): Promise<string> {
const storageKey = await this.getStorageKey()
const local = await this.getMapWithoutDeleted(storageKey)

return getValidatorQueryString([...local.values()], 2000, (await this.merchantUtils.getCurrentPlanMaxValidator()) - 1)
}

async getAllMyValidators(): Promise<Validator[]> {
const storageKey = await this.getStorageKey()
const local = await this.getMapWithoutDeleted(storageKey)
Expand Down

0 comments on commit a736199

Please sign in to comment.