@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
94 lines • 5 kB
JavaScript
import { ForkName, WHISTLEBLOWER_REWARD_QUOTIENT, WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA, isForkPostElectra, } from "@lodestar/params";
import { getAttesterSlashableIndices, processAttestationsAltair, } from "@lodestar/state-transition";
/**
* Calculate total proposer block rewards given block and the beacon state of the same slot before the block is applied (preState)
* postState can be passed in to read reward cache if available
* Standard (Non MEV) rewards for proposing a block consists of:
* 1) Including attestations from (beacon) committee
* 2) Including attestations from sync committee
* 3) Reporting slashable behaviours from proposer and attester
*/
export async function computeBlockRewards(block, preState, postState) {
const fork = preState.config.getForkName(block.slot);
const { attestations: cachedAttestationsReward = 0, syncAggregate: cachedSyncAggregateReward = 0 } = postState?.proposerRewards ?? {};
let blockAttestationReward = cachedAttestationsReward;
let syncAggregateReward = cachedSyncAggregateReward;
if (blockAttestationReward === 0) {
blockAttestationReward =
fork === ForkName.phase0
? computeBlockAttestationRewardPhase0(block, preState)
: computeBlockAttestationRewardAltair(block, preState);
}
if (syncAggregateReward === 0) {
syncAggregateReward = computeSyncAggregateReward(block, preState);
}
const blockProposerSlashingReward = computeBlockProposerSlashingReward(fork, block, preState);
const blockAttesterSlashingReward = computeBlockAttesterSlashingReward(fork, block, preState);
const total = blockAttestationReward + syncAggregateReward + blockProposerSlashingReward + blockAttesterSlashingReward;
return {
proposerIndex: block.proposerIndex,
total,
attestations: blockAttestationReward,
syncAggregate: syncAggregateReward,
proposerSlashings: blockProposerSlashingReward,
attesterSlashings: blockAttesterSlashingReward,
};
}
/**
* TODO: Calculate rewards received by block proposer for including attestations.
*/
function computeBlockAttestationRewardPhase0(_block, _preState) {
throw new Error("Unsupported fork! Block attestation reward calculation is not available in phase0");
}
/**
* Calculate rewards received by block proposer for including attestations since Altair.
* Reuses `processAttestationsAltair()`. Has dependency on RewardCache
*/
function computeBlockAttestationRewardAltair(block, preState) {
const fork = preState.config.getForkSeq(block.slot);
const { attestations } = block.body;
processAttestationsAltair(fork, preState, attestations, false);
return preState.proposerRewards.attestations;
}
function computeSyncAggregateReward(block, preState) {
if (block.body.syncAggregate !== undefined) {
const { syncCommitteeBits } = block.body.syncAggregate;
const { syncProposerReward } = preState.epochCtx;
return syncCommitteeBits.getTrueBitIndexes().length * Math.floor(syncProposerReward); // syncProposerReward should already be integer
}
return 0; // phase0 block does not have syncAggregate
}
/**
* Calculate rewards received by block proposer for including proposer slashings.
* All proposer slashing rewards go to block proposer and none to whistleblower as of Deneb
*/
function computeBlockProposerSlashingReward(fork, block, state) {
let proposerSlashingReward = 0;
for (const proposerSlashing of block.body.proposerSlashings) {
const offendingProposerIndex = proposerSlashing.signedHeader1.message.proposerIndex;
const offendingProposerBalance = state.validators.getReadonly(offendingProposerIndex).effectiveBalance;
const whistleblowerRewardQuotient = isForkPostElectra(fork)
? WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA
: WHISTLEBLOWER_REWARD_QUOTIENT;
proposerSlashingReward += Math.floor(offendingProposerBalance / whistleblowerRewardQuotient);
}
return proposerSlashingReward;
}
/**
* Calculate rewards received by block proposer for including attester slashings.
* All attester slashing rewards go to block proposer and none to whistleblower as of Deneb
*/
function computeBlockAttesterSlashingReward(fork, block, preState) {
let attesterSlashingReward = 0;
for (const attesterSlashing of block.body.attesterSlashings) {
for (const offendingAttesterIndex of getAttesterSlashableIndices(attesterSlashing)) {
const offendingAttesterBalance = preState.validators.getReadonly(offendingAttesterIndex).effectiveBalance;
const whistleblowerRewardQuotient = isForkPostElectra(fork)
? WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA
: WHISTLEBLOWER_REWARD_QUOTIENT;
attesterSlashingReward += Math.floor(offendingAttesterBalance / whistleblowerRewardQuotient);
}
}
return attesterSlashingReward;
}
//# sourceMappingURL=blockRewards.js.map