Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: sync committee accounting #272

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/duty/duty.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export class DutyService {
const perSyncProposerReward = Math.floor((meta.sync.per_block_reward * PROPOSER_WEIGHT) / (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT));
const maxBatchSize = 1000;
let index = 0;

for (const v of this.summary.epoch(epoch).values()) {
const effectiveBalance = v.val_effective_balance;
const increments = Number(effectiveBalance / BigInt(10 ** 9));
Expand All @@ -135,15 +136,19 @@ export class DutyService {
}
}
if (v.is_sync) {
for (const block of v.sync_meta.synced_blocks) {
meta.sync.blocks_rewards.set(block, meta.sync.blocks_rewards.get(block) + BigInt(perSyncProposerReward));
for (const syncMetaItem of v.sync_meta) {
for (const block of syncMetaItem.synced_blocks) {
const blockRewards = meta.sync.blocks_rewards.get(block);
meta.sync.blocks_rewards.set(block, blockRewards + BigInt(perSyncProposerReward));
}
}
}
index++;
if (index % maxBatchSize == 0) {
await unblock();
}
}

this.summary.epoch(epoch).setMeta(meta);
}

Expand Down
6 changes: 3 additions & 3 deletions src/duty/summary/summary.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export interface ValidatorDutySummary {
att_valid_source?: boolean;
// Metadata. Necessary for calculating rewards and will not be stored in DB
sync_meta?: {
synced_blocks?: number[];
};
synced_blocks: number[];
}[];
// Rewards
att_earned_reward?: number;
att_missed_reward?: number;
Expand Down Expand Up @@ -100,7 +100,7 @@ export class SummaryService {
const curr = epochStorageData.summary.get(val.val_id) ?? {};
epochStorageData.summary.set(val.val_id, merge(curr, val));
},
get: (val_id: ValidatorId): ValidatorDutySummary => {
get: (val_id: ValidatorId): ValidatorDutySummary | undefined => {
return epochStorageData.summary.get(val_id);
},
values: () => {
Expand Down
28 changes: 19 additions & 9 deletions src/duty/sync/sync.rewards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,27 @@ export class SyncRewards {

public async calculate(epoch: Epoch) {
const epochMeta = this.summary.epoch(epoch).getMeta();
let sync_earned_reward = 0;
let sync_missed_reward = 0;
let sync_penalty = 0;
const perfectSync = epochMeta.sync.per_block_reward * epochMeta.sync.blocks_to_sync.length;
for (const v of this.summary.epoch(epoch).values()) {
if (!v.is_sync) continue;
sync_earned_reward = epochMeta.sync.per_block_reward * v.sync_meta.synced_blocks.length;
sync_penalty = epochMeta.sync.per_block_reward * (epochMeta.sync.blocks_to_sync.length - v.sync_meta.synced_blocks.length);
sync_missed_reward = perfectSync - sync_earned_reward;
if (!v.is_sync) {
continue;
}

this.summary.epoch(epoch).set({ epoch, val_id: v.val_id, sync_earned_reward, sync_penalty, sync_missed_reward });
const totalBlocksToSync = epochMeta.sync.blocks_to_sync.length * v.sync_meta.length;
const perfectSync = epochMeta.sync.per_block_reward * totalBlocksToSync;

let totalSyncedBlocksCount = 0;
for (const syncMetaItem of v.sync_meta) {
totalSyncedBlocksCount += syncMetaItem.synced_blocks.length;
}

const syncEarnedReward = epochMeta.sync.per_block_reward * totalSyncedBlocksCount;
this.summary.epoch(epoch).set({
epoch,
val_id: v.val_id,
sync_earned_reward: syncEarnedReward,
sync_penalty: epochMeta.sync.per_block_reward * (totalBlocksToSync - totalSyncedBlocksCount),
sync_missed_reward: perfectSync - syncEarnedReward,
});
}
}
}
64 changes: 46 additions & 18 deletions src/duty/sync/sync.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { BitVectorType, fromHexString } from '@chainsafe/ssz';
import { LOGGER_PROVIDER } from '@lido-nestjs/logger';
import { Inject, Injectable, LoggerService } from '@nestjs/common';
import { concat, sumBy } from 'lodash';

import { ConfigService } from 'common/config';
import { BlockInfoResponse, ConsensusProviderService, SyncCommitteeValidator } from 'common/consensus-provider';
import { Epoch, Slot, StateId } from 'common/consensus-provider/types';
import { PrometheusService, TrackTask } from 'common/prometheus';
import { SummaryService } from 'duty/summary';
import { SummaryService, ValidatorDutySummary } from 'duty/summary';

import { SYNC_COMMITTEE_SIZE } from './sync.constants';

Expand All @@ -22,44 +23,71 @@ export class SyncService {

@TrackTask('check-sync-duties')
public async check(epoch: Epoch, stateSlot: Slot): Promise<void> {
this.logger.log(`Getting sync committee participation info`);
const SyncCommitteeBits = new BitVectorType(SYNC_COMMITTEE_SIZE); // sync participants count in committee
this.logger.log(`Getting sync committee participation info for state slot ${stateSlot}`);
const syncCommitteeBits = new BitVectorType(SYNC_COMMITTEE_SIZE); // sync participants count in committee
const indexedValidators = await this.getSyncCommitteeIndexedValidators(epoch, stateSlot);
this.logger.log(`Processing sync committee participation info`);
const epochBlocks: BlockInfoResponse[] = [];
const missedSlots: number[] = [];
const startSlot = epoch * this.config.get('FETCH_INTERVAL_SLOTS');
for (let slot = startSlot; slot < startSlot + this.config.get('FETCH_INTERVAL_SLOTS'); slot = slot + 1) {
const blockInfo = await this.clClient.getBlockInfo(slot);
blockInfo ? epochBlocks.push(blockInfo) : missedSlots.push(slot);
if (blockInfo) {
epochBlocks.push(blockInfo);
} else {
missedSlots.push(slot);
}
}

this.logger.debug(`All missed slots in getting sync committee info process: ${missedSlots}`);
/**
* @todo We should investigate what happens in this and other places of the code if this array is empty,
* and fix all possible issues.
*/
const epochBlocksBits = epochBlocks.map((block) => {
return {
block: Number(block.message.slot),
bits: SyncCommitteeBits.deserialize(fromHexString(block.message.body.sync_aggregate.sync_committee_bits)),
bits: syncCommitteeBits.deserialize(fromHexString(block.message.body.sync_aggregate.sync_committee_bits)),
};
});

for (const indexedValidator of indexedValidators) {
const synced_blocks: number[] = [];
const syncedBlocks: number[] = [];
for (const blockBits of epochBlocksBits) {
if (blockBits.bits.get(indexedValidator.in_committee_index)) {
synced_blocks.push(blockBits.block);
syncedBlocks.push(blockBits.block);
}
}

const index = Number(indexedValidator.validator_index);
const percent = (synced_blocks.length / epochBlocksBits.length) * 100;
this.summary.epoch(epoch).set({
epoch,
val_id: index,
is_sync: true,
sync_percent: percent,
sync_meta: {
synced_blocks,
},
});
const summaryValidator = this.summary.epoch(epoch).get(index);
const newSummaryValidator: ValidatorDutySummary = summaryValidator?.is_sync
? {
epoch,
val_id: index,
sync_meta: concat(summaryValidator.sync_meta, {
synced_blocks: syncedBlocks,
}),
}
: {
epoch,
val_id: index,
is_sync: true,
sync_meta: [{ synced_blocks: syncedBlocks }],
};

const totalSyncedBlocks = sumBy(newSummaryValidator.sync_meta, (blocks) => blocks.synced_blocks.length);
const totalBlocksToSync = epochBlocksBits.length * newSummaryValidator.sync_meta.length;
newSummaryValidator.sync_percent = (totalSyncedBlocks / totalBlocksToSync) * 100;

this.summary.epoch(epoch).set(newSummaryValidator);
}
this.summary.epoch(epoch).setMeta({ sync: { blocks_to_sync: epochBlocksBits.map((b) => b.block) } });

this.summary.epoch(epoch).setMeta({
sync: {
blocks_to_sync: epochBlocksBits.map((b) => b.block),
},
});
}

public async getSyncCommitteeIndexedValidators(epoch: Epoch, stateId: StateId): Promise<SyncCommitteeValidator[]> {
Expand Down
Loading