Skip to content

Commit

Permalink
Compute executionOptimistic for beacon/blocks routes (#4943)
Browse files Browse the repository at this point in the history
  • Loading branch information
dapplion authored Dec 24, 2022
1 parent d7f0168 commit c274d22
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 25 deletions.
42 changes: 27 additions & 15 deletions packages/beacon-node/src/api/impl/beacon/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import {eip4844} from "@lodestar/types";
import {fromHexString, toHexString} from "@chainsafe/ssz";
import {getBlockInput} from "../../../../chain/blocks/types.js";
import {promiseAllMaybeAsync} from "../../../../util/promises.js";
import {isOptimisticBlock} from "../../../../util/forkChoice.js";
import {BlockError, BlockErrorCode} from "../../../../chain/errors/index.js";
import {OpSource} from "../../../../metrics/validatorMonitor.js";
import {NetworkEvent} from "../../../../network/index.js";
import {ApiModules, IS_OPTIMISTIC_TEMP} from "../../types.js";
import {ApiModules} from "../../types.js";
import {resolveBlockId, toBeaconHeaderResponse} from "./utils.js";

/**
Expand All @@ -29,6 +30,9 @@ export function getBeaconBlockApi({
async getBlockHeaders(filters) {
// TODO - SLOW CODE: This code seems like it could be improved

// If one block in the response contains an optimistic block, mark the entire response as optimistic
let executionOptimistic = false;

const result: routes.beacon.BlockHeaderResponse[] = [];
if (filters.parentRoot) {
const parentRoot = filters.parentRoot;
Expand All @@ -44,12 +48,15 @@ export function getBeaconBlockApi({
const cannonical = chain.forkChoice.getCanonicalBlockAtSlot(block.message.slot);
if (cannonical) {
result.push(toBeaconHeaderResponse(config, block, cannonical.blockRoot === summary.blockRoot));
if (isOptimisticBlock(cannonical)) {
executionOptimistic = true;
}
}
}
})
);
return {
executionOptimistic: IS_OPTIMISTIC_TEMP,
executionOptimistic,
data: result.filter(
(item) =>
// skip if no slot filter
Expand Down Expand Up @@ -83,6 +90,10 @@ export function getBeaconBlockApi({
// TODO: What is this logic?
await Promise.all(
chain.forkChoice.getBlockSummariesAtSlot(filters.slot).map(async (summary) => {
if (isOptimisticBlock(summary)) {
executionOptimistic = true;
}

if (summary.blockRoot !== toHexString(canonicalRoot)) {
const block = await db.block.get(fromHexString(summary.blockRoot));
if (block) {
Expand All @@ -94,38 +105,39 @@ export function getBeaconBlockApi({
}

return {
executionOptimistic: IS_OPTIMISTIC_TEMP,
executionOptimistic,
data: result,
};
},

async getBlockHeader(blockId) {
const block = await resolveBlockId(chain.forkChoice, db, blockId);
const {block, executionOptimistic} = await resolveBlockId(chain.forkChoice, db, blockId);
return {
executionOptimistic: IS_OPTIMISTIC_TEMP,
executionOptimistic,
data: toBeaconHeaderResponse(config, block, true),
};
},

async getBlock(blockId) {
const {block} = await resolveBlockId(chain.forkChoice, db, blockId);
return {
data: await resolveBlockId(chain.forkChoice, db, blockId),
data: block,
};
},

async getBlockV2(blockId) {
const block = await resolveBlockId(chain.forkChoice, db, blockId);
const {block, executionOptimistic} = await resolveBlockId(chain.forkChoice, db, blockId);
return {
executionOptimistic: IS_OPTIMISTIC_TEMP,
executionOptimistic,
data: block,
version: config.getForkName(block.message.slot),
};
},

async getBlockAttestations(blockId) {
const block = await resolveBlockId(chain.forkChoice, db, blockId);
const {block, executionOptimistic} = await resolveBlockId(chain.forkChoice, db, blockId);
return {
executionOptimistic: IS_OPTIMISTIC_TEMP,
executionOptimistic,
data: Array.from(block.message.body.attestations),
};
},
Expand All @@ -138,30 +150,30 @@ export function getBeaconBlockApi({

if (slot === head.slot) {
return {
executionOptimistic: IS_OPTIMISTIC_TEMP,
executionOptimistic: isOptimisticBlock(head),
data: {root: fromHexString(head.blockRoot)},
};
}

if (slot < head.slot && head.slot <= slot + SLOTS_PER_HISTORICAL_ROOT) {
const state = chain.getHeadState();
return {
executionOptimistic: IS_OPTIMISTIC_TEMP,
executionOptimistic: isOptimisticBlock(head),
data: {root: state.blockRoots.get(slot % SLOTS_PER_HISTORICAL_ROOT)},
};
}
} else if (blockId === "head") {
const head = chain.forkChoice.getHead();
return {
executionOptimistic: IS_OPTIMISTIC_TEMP,
executionOptimistic: isOptimisticBlock(head),
data: {root: fromHexString(head.blockRoot)},
};
}

// Slow path
const block = await resolveBlockId(chain.forkChoice, db, blockId);
const {block, executionOptimistic} = await resolveBlockId(chain.forkChoice, db, blockId);
return {
executionOptimistic: IS_OPTIMISTIC_TEMP,
executionOptimistic,
data: {root: config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message)},
};
},
Expand Down
39 changes: 29 additions & 10 deletions packages/beacon-node/src/api/impl/beacon/blocks/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {fromHexString} from "@chainsafe/ssz";
import {IBeaconDb} from "../../../../db/index.js";
import {GENESIS_SLOT} from "../../../../constants/index.js";
import {ApiError, ValidationError} from "../../errors.js";
import {isOptimisticBlock} from "../../../../util/forkChoice.js";

export function toBeaconHeaderResponse(
config: IChainForkConfig,
Expand All @@ -27,32 +28,41 @@ export async function resolveBlockId(
forkChoice: IForkChoice,
db: IBeaconDb,
blockId: routes.beacon.BlockId
): Promise<allForks.SignedBeaconBlock> {
const block = await resolveBlockIdOrNull(forkChoice, db, blockId);
): Promise<{block: allForks.SignedBeaconBlock; executionOptimistic: boolean}> {
const {block, executionOptimistic} = await resolveBlockIdOrNull(forkChoice, db, blockId);
if (!block) {
throw new ApiError(404, `No block found for id '${blockId}'`);
}

return block;
return {block, executionOptimistic};
}

async function resolveBlockIdOrNull(
forkChoice: IForkChoice,
db: IBeaconDb,
blockId: routes.beacon.BlockId
): Promise<allForks.SignedBeaconBlock | null> {
): Promise<{block: allForks.SignedBeaconBlock | null; executionOptimistic: boolean}> {
blockId = String(blockId).toLowerCase();
if (blockId === "head") {
const head = forkChoice.getHead();
return db.block.get(fromHexString(head.blockRoot));
return {
block: await db.block.get(fromHexString(head.blockRoot)),
executionOptimistic: isOptimisticBlock(head),
};
}

if (blockId === "genesis") {
return db.blockArchive.get(GENESIS_SLOT);
return {
block: await db.blockArchive.get(GENESIS_SLOT),
executionOptimistic: false,
};
}

if (blockId === "finalized") {
return await db.blockArchive.get(forkChoice.getFinalizedBlock().slot);
return {
block: await db.blockArchive.get(forkChoice.getFinalizedBlock().slot),
executionOptimistic: false,
};
}

let blockSummary;
Expand All @@ -77,12 +87,21 @@ async function resolveBlockIdOrNull(
// Unfinalized blocks are stored in the block repository, but the finalized block is in the block archive
const finalized = forkChoice.getFinalizedBlock();
if (blockSummary.slot === finalized.slot) {
return await db.blockArchive.get(finalized.slot);
return {
block: await db.blockArchive.get(finalized.slot),
executionOptimistic: isOptimisticBlock(blockSummary),
};
} else {
return await db.block.get(fromHexString(blockSummary.blockRoot));
return {
block: await db.block.get(fromHexString(blockSummary.blockRoot)),
executionOptimistic: false,
};
}
} else {
// Blocks not in the fork choice are in the block archive
return await getBlockByBlockArchive();
return {
block: await getBlockByBlockArchive(),
executionOptimistic: false,
};
}
}

0 comments on commit c274d22

Please sign in to comment.