Skip to content

Commit

Permalink
Update rewards routes and server (#6178 and #6260)
Browse files Browse the repository at this point in the history
  • Loading branch information
nflaig committed Mar 10, 2024
1 parent 013d078 commit b7f3f14
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 106 deletions.
5 changes: 4 additions & 1 deletion packages/api/src/beacon/routes/beacon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export * as state from "./state.js";
export * as rewards from "./rewards.js";
export {BroadcastValidation} from "./block.js";
export type {BlockId, BlockHeaderResponse} from "./block.js";
export type {BlockRewards, SyncCommitteeRewards} from "./rewards.js";
// TODO: Review if re-exporting all these types is necessary
export type {
StateId,
Expand All @@ -32,7 +33,8 @@ export type {

export type Endpoints = block.Endpoints &
pool.Endpoints &
state.Endpoints & {
state.Endpoints &
rewards.Endpoints & {
getGenesis: Endpoint<
//
"GET",
Expand All @@ -57,5 +59,6 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
...block.getDefinitions(config),
...pool.definitions,
...state.definitions,
...rewards.definitions,
};
}
174 changes: 75 additions & 99 deletions packages/api/src/beacon/routes/beacon/rewards.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,67 @@
import {ContainerType} from "@chainsafe/ssz";
import {ssz, ValidatorIndex} from "@lodestar/types";
/* eslint-disable @typescript-eslint/naming-convention */
import {ContainerType, ValueOf} from "@chainsafe/ssz";
import {ssz} from "@lodestar/types";

import {
RoutesData,
ReturnTypes,
Schema,
ReqSerializers,
ContainerDataExecutionOptimistic,
ArrayOf,
} from "../../../utils/index.js";
import {HttpStatusCode} from "../../../utils/client/httpStatusCode.js";
import {ApiClientResponse} from "../../../interfaces.js";
import {Schema, Endpoint, RouteDefinitions} from "../../../utils/index.js";
import {ExecutionOptimisticCodec, ExecutionOptimisticMeta, JsonOnlyReq} from "../../../utils/codecs.js";
import {BlockId} from "./block.js";
import {ValidatorId} from "./state.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;
const BlockRewardsType = new ContainerType(
{
/** Proposer of the block, the proposer index who receives these rewards */
proposerIndex: ssz.ValidatorIndex,
/** Total block reward, equal to attestations + sync_aggregate + proposer_slashings + attester_slashings */
total: ssz.UintNum64,
/** Block reward component due to included attestations */
attestations: ssz.UintNum64,
/** Block reward component due to included sync_aggregate */
syncAggregate: ssz.UintNum64,
/** Block reward component due to included proposer_slashings */
proposerSlashings: ssz.UintNum64,
/** Block reward component due to included attester_slashings */
attesterSlashings: ssz.UintNum64,
},
{jsonCase: "eth2"}
);

const SyncCommitteeRewardsType = new ContainerType(
{
validatorIndex: ssz.ValidatorIndex,
reward: ssz.UintNum64,
},
{jsonCase: "eth2"}
);

/**
* Rewards info for a single block. Every reward value is in Gwei.
*/
export type BlockRewards = {
/** 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 BlockRewards = ValueOf<typeof BlockRewardsType>;

/**
* Rewards info for sync committee participation. Every reward value is in Gwei.
* Note: In the case that block proposer is present in `SyncCommitteeRewards`, the reward value only reflects rewards for
* participating in sync committee. Please refer to `BlockRewards.syncAggregate` for rewards of proposer including sync committee
* outputs into their block
*/
export type SyncCommitteeRewards = {validatorIndex: ValidatorIndex; reward: number}[];
export type SyncCommitteeRewards = ValueOf<typeof SyncCommitteeRewardsType>;

export type Api = {
export type Endpoints = {
/**
* 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
>
getBlockRewards: Endpoint<
"GET",
{blockId: BlockId},
{params: {block_id: string}},
BlockRewards,
ExecutionOptimisticMeta
>;

/**
Expand All @@ -73,72 +72,49 @@ export type Api = {
* Can be one of: "head" (canonical head in node's view), "genesis", "finalized", \<slot\>, \<hex encoded blockRoot with 0x prefix\>.
* @param validatorIds List of validator indices or pubkeys to filter in
*/
getSyncCommitteeRewards(
blockId: BlockId,
validatorIds?: ValidatorId[]
): Promise<
ApiClientResponse<
{[HttpStatusCode.OK]: {data: SyncCommitteeRewards; executionOptimistic: ExecutionOptimistic}},
HttpStatusCode.BAD_REQUEST | HttpStatusCode.NOT_FOUND
>
getSyncCommitteeRewards: Endpoint<
"POST",
{blockId: BlockId; validatorIds?: ValidatorId[]},
{params: {block_id: string}; body: ValidatorId[]},
SyncCommitteeRewards,
ExecutionOptimisticMeta
>;
};

/**
* Define javascript values for each route
*/
export const routesData: RoutesData<Api> = {
getBlockRewards: {url: "/eth/v1/beacon/rewards/blocks/{block_id}", method: "GET"},
getSyncCommitteeRewards: {url: "/eth/v1/beacon/rewards/sync_committee/{block_id}", method: "POST"},
};

export type ReqTypes = {
/* eslint-disable @typescript-eslint/naming-convention */
getBlockRewards: {params: {block_id: string}};
getSyncCommitteeRewards: {params: {block_id: string}; body: ValidatorId[]};
};

export function getReqSerializers(): ReqSerializers<Api, ReqTypes> {
return {
getBlockRewards: {
writeReq: (block_id) => ({params: {block_id: String(block_id)}}),
parseReq: ({params}) => [params.block_id],
export const definitions: RouteDefinitions<Endpoints> = {
getBlockRewards: {
url: "/eth/v1/beacon/rewards/blocks/{block_id}",
method: "GET",
req: {
writeReq: ({blockId}) => ({params: {block_id: String(blockId)}}),
parseReq: ({params}) => ({blockId: params.block_id}),
schema: {params: {block_id: Schema.StringRequired}},
},
getSyncCommitteeRewards: {
writeReq: (block_id, validatorIds) => ({params: {block_id: String(block_id)}, body: validatorIds || []}),
parseReq: ({params, body}) => [params.block_id, body],
resp: {
data: BlockRewardsType,
meta: ExecutionOptimisticCodec,
},
},
getSyncCommitteeRewards: {
url: "/eth/v1/beacon/rewards/sync_committee/{block_id}",
method: "POST",
req: JsonOnlyReq({
writeReqJson: ({blockId, validatorIds}) => ({
params: {block_id: String(blockId)},
body: validatorIds || [],
}),
parseReqJson: ({params, body}) => ({
blockId: params.block_id,
validatorIds: body,
}),
schema: {
params: {block_id: Schema.StringRequired},
body: Schema.UintOrStringArray,
},
}),
resp: {
data: SyncCommitteeRewardsType,
meta: ExecutionOptimisticCodec,
},
};
}

export function getReturnTypes(): ReturnTypes<Api> {
const BlockRewardsResponse = new ContainerType(
{
proposerIndex: ssz.ValidatorIndex,
total: ssz.UintNum64,
attestations: ssz.UintNum64,
syncAggregate: ssz.UintNum64,
proposerSlashings: ssz.UintNum64,
attesterSlashings: ssz.UintNum64,
},
{jsonCase: "eth2"}
);

const SyncCommitteeRewardsResponse = new ContainerType(
{
validatorIndex: ssz.ValidatorIndex,
reward: ssz.UintNum64,
},
{jsonCase: "eth2"}
);

return {
getBlockRewards: ContainerDataExecutionOptimistic(BlockRewardsResponse),
getSyncCommitteeRewards: ContainerDataExecutionOptimistic(ArrayOf(SyncCommitteeRewardsResponse)),
};
}
},
};
14 changes: 8 additions & 6 deletions packages/beacon-node/src/api/impl/beacon/rewards/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import {routes, ServerApi} from "@lodestar/api";
import {ApplicationMethods, routes} 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> {
export function getBeaconRewardsApi({
chain,
}: Pick<ApiModules, "chain">): ApplicationMethods<routes.beacon.rewards.Endpoints> {
return {
async getBlockRewards(blockId) {
async getBlockRewards({blockId}) {
const {block, executionOptimistic} = await resolveBlockId(chain, blockId);
const data = await chain.getBlockRewards(block.message);
return {data, executionOptimistic};
return {data, meta: {executionOptimistic}};
},
async getSyncCommitteeRewards(blockId, validatorIds) {
async getSyncCommitteeRewards({blockId, validatorIds}) {
const {block, executionOptimistic} = await resolveBlockId(chain, blockId);
const data = await chain.getSyncCommitteeRewards(block.message, validatorIds);
return {data, executionOptimistic};
return {data, meta: {executionOptimistic}};
},
};
}

0 comments on commit b7f3f14

Please sign in to comment.