-
-
Notifications
You must be signed in to change notification settings - Fork 445
feat: add endpoint for Altair block reward #6178
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
Changes from all commits
6679119
f67421b
0b030f2
5e3f0f5
c2da457
1e47858
ac9bb42
1fc3201
f702c2b
6c14d0c
f331e5a
db5cc41
edc8c3c
f543045
b605529
45734fa
f80daf4
3f8d42c
f2bbe95
cde10e9
a6e2fc4
ee42dde
b6f68d0
5c10acf
47e9650
719ab1d
51bbf5e
eceea02
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| import {ContainerType} from "@chainsafe/ssz"; | ||
| import {ssz, ValidatorIndex} from "@lodestar/types"; | ||
|
|
||
| import { | ||
| RoutesData, | ||
| ReturnTypes, | ||
| Schema, | ||
| ReqSerializers, | ||
| ContainerDataExecutionOptimistic, | ||
| } from "../../../utils/index.js"; | ||
| import {HttpStatusCode} from "../../../utils/client/httpStatusCode.js"; | ||
| import {ApiClientResponse} from "../../../interfaces.js"; | ||
| import {BlockId} from "./block.js"; | ||
|
|
||
| // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes | ||
|
|
||
| /** | ||
| * True if the response references an unverified execution payload. Optimistic information may be invalidated at | ||
| * a later time. If the field is not present, assume the False value. | ||
| */ | ||
| export type ExecutionOptimistic = boolean; | ||
|
|
||
| /** | ||
| * Rewards info for a single block. Every reward value is in Gwei. | ||
| */ | ||
| export type BlockRewards = { | ||
ensi321 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** Proposer of the block, the proposer index who receives these rewards */ | ||
| proposerIndex: ValidatorIndex; | ||
| /** Total block reward, equal to attestations + sync_aggregate + proposer_slashings + attester_slashings */ | ||
| total: number; | ||
| /** Block reward component due to included attestations */ | ||
| attestations: number; | ||
| /** Block reward component due to included sync_aggregate */ | ||
| syncAggregate: number; | ||
| /** Block reward component due to included proposer_slashings */ | ||
| proposerSlashings: number; | ||
| /** Block reward component due to included attester_slashings */ | ||
| attesterSlashings: number; | ||
| }; | ||
|
|
||
| export type Api = { | ||
| /** | ||
| * Get block rewards | ||
| * Returns the info of rewards received by the block proposer | ||
| * | ||
| * @param blockId Block identifier. | ||
| * Can be one of: "head" (canonical head in node's view), "genesis", "finalized", \<slot\>, \<hex encoded blockRoot with 0x prefix\>. | ||
| */ | ||
| getBlockRewards( | ||
| blockId: BlockId | ||
| ): Promise< | ||
| ApiClientResponse< | ||
| {[HttpStatusCode.OK]: {data: BlockRewards; executionOptimistic: ExecutionOptimistic}}, | ||
| HttpStatusCode.BAD_REQUEST | HttpStatusCode.NOT_FOUND | ||
| > | ||
| >; | ||
| }; | ||
|
|
||
| /** | ||
| * Define javascript values for each route | ||
| */ | ||
| export const routesData: RoutesData<Api> = { | ||
| getBlockRewards: {url: "/eth/v1/beacon/rewards/blocks/{block_id}", method: "GET"}, | ||
| }; | ||
|
|
||
| export type ReqTypes = { | ||
| /* eslint-disable @typescript-eslint/naming-convention */ | ||
| getBlockRewards: {params: {block_id: string}}; | ||
| }; | ||
|
|
||
| export function getReqSerializers(): ReqSerializers<Api, ReqTypes> { | ||
| return { | ||
| getBlockRewards: { | ||
| writeReq: (block_id) => ({params: {block_id: String(block_id)}}), | ||
| parseReq: ({params}) => [params.block_id], | ||
| schema: {params: {block_id: Schema.StringRequired}}, | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export function getReturnTypes(): ReturnTypes<Api> { | ||
| const BlockRewardsResponse = new ContainerType( | ||
| { | ||
| proposerIndex: ssz.ValidatorIndex, | ||
| total: ssz.UintNum64, | ||
| attestations: ssz.UintNum64, | ||
nflaig marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| syncAggregate: ssz.UintNum64, | ||
| proposerSlashings: ssz.UintNum64, | ||
| attesterSlashings: ssz.UintNum64, | ||
| }, | ||
| {jsonCase: "eth2"} | ||
| ); | ||
|
|
||
| return { | ||
| getBlockRewards: ContainerDataExecutionOptimistic(BlockRewardsResponse), | ||
| }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import {routes, ServerApi} from "@lodestar/api"; | ||
| import {ApiModules} from "../../types.js"; | ||
| import {resolveBlockId} from "../blocks/utils.js"; | ||
|
|
||
| export function getBeaconRewardsApi({chain}: Pick<ApiModules, "chain">): ServerApi<routes.beacon.rewards.Api> { | ||
| return { | ||
| async getBlockRewards(blockId) { | ||
| const {block, executionOptimistic} = await resolveBlockId(chain, blockId); | ||
| const data = await chain.getBlockRewards(block.message); | ||
| return {data, executionOptimistic}; | ||
| }, | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,6 +71,40 @@ export class QueuedStateRegenerator implements IStateRegenerator { | |
| return this.stateCache.get(stateRoot); | ||
| } | ||
|
|
||
| getPreStateSync(block: allForks.BeaconBlock): CachedBeaconStateAllForks | null { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please take a closer look at
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes looks good to me |
||
| const parentRoot = toHexString(block.parentRoot); | ||
| const parentBlock = this.forkChoice.getBlockHex(parentRoot); | ||
| if (!parentBlock) { | ||
| throw new RegenError({ | ||
| code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE, | ||
| blockRoot: block.parentRoot, | ||
| }); | ||
| } | ||
|
|
||
| const parentEpoch = computeEpochAtSlot(parentBlock.slot); | ||
| const blockEpoch = computeEpochAtSlot(block.slot); | ||
|
|
||
| // Check the checkpoint cache (if the pre-state is a checkpoint state) | ||
| if (parentEpoch < blockEpoch) { | ||
| const checkpointState = this.checkpointStateCache.getLatest(parentRoot, blockEpoch); | ||
| if (checkpointState && computeEpochAtSlot(checkpointState.slot) === blockEpoch) { | ||
| return checkpointState; | ||
| } | ||
| } | ||
|
|
||
| // Check the state cache, only if the state doesn't need to go through an epoch transition. | ||
| // Otherwise the state transition may not be cached and wasted. Queue for regen since the | ||
| // work required will still be significant. | ||
| if (parentEpoch === blockEpoch) { | ||
| const state = this.stateCache.get(parentBlock.stateRoot); | ||
| if (state) { | ||
| return state; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| getCheckpointStateSync(cp: CheckpointHex): CachedBeaconStateAllForks | null { | ||
| return this.checkpointStateCache.get(cp); | ||
| } | ||
|
|
@@ -137,34 +171,10 @@ export class QueuedStateRegenerator implements IStateRegenerator { | |
| this.metrics?.regenFnCallTotal.inc({caller: rCaller, entrypoint: RegenFnName.getPreState}); | ||
|
|
||
| // First attempt to fetch the state from caches before queueing | ||
| const parentRoot = toHexString(block.parentRoot); | ||
| const parentBlock = this.forkChoice.getBlockHex(parentRoot); | ||
| if (!parentBlock) { | ||
| throw new RegenError({ | ||
| code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE, | ||
| blockRoot: block.parentRoot, | ||
| }); | ||
| } | ||
| const cachedState = this.getPreStateSync(block); | ||
|
|
||
| const parentEpoch = computeEpochAtSlot(parentBlock.slot); | ||
| const blockEpoch = computeEpochAtSlot(block.slot); | ||
|
|
||
| // Check the checkpoint cache (if the pre-state is a checkpoint state) | ||
| if (parentEpoch < blockEpoch) { | ||
| const checkpointState = this.checkpointStateCache.getLatest(parentRoot, blockEpoch); | ||
| if (checkpointState && computeEpochAtSlot(checkpointState.slot) === blockEpoch) { | ||
| return checkpointState; | ||
| } | ||
| } | ||
|
|
||
| // Check the state cache, only if the state doesn't need to go through an epoch transition. | ||
| // Otherwise the state transition may not be cached and wasted. Queue for regen since the | ||
| // work required will still be significant. | ||
| if (parentEpoch === blockEpoch) { | ||
| const state = this.stateCache.get(parentBlock.stateRoot); | ||
| if (state) { | ||
| return state; | ||
| } | ||
| if (cachedState !== null) { | ||
| return cachedState; | ||
| } | ||
|
|
||
| // The state is not immediately available in the caches, enqueue the job | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.