Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
12 changes: 12 additions & 0 deletions packages/beacon-node/src/chain/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,18 @@ export class BeaconChain implements IBeaconChain {
return null;
}

async getSerializedExecutionPayloadEnvelope(blockRootHex: string): Promise<Uint8Array | null> {
// TODO GLOAS: Check SeenPayloadEnvelopeInput once one it is available
Comment thread
ensi321 marked this conversation as resolved.
Resolved
// If SeenPayloadEnvelopeInput has the payload, try look up serializedCache to avoid serialization.
// If serializedCache doesn't have it, serialize and return.

const unfinalizedEnvelope = await this.db.executionPayloadEnvelope.getBinary(fromHex(blockRootHex));
if (unfinalizedEnvelope) {
return unfinalizedEnvelope;
}
return null;
Comment thread
ensi321 marked this conversation as resolved.
Resolved
}

async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<DataColumnSidecars> {
const blockInput = this.seenBlockInputCache.get(blockRootHex);
if (blockInput) {
Expand Down
1 change: 1 addition & 0 deletions packages/beacon-node/src/chain/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ export interface IBeaconChain {
blockRootHex: string,
indices: number[]
): Promise<(Uint8Array | undefined)[]>;
getSerializedExecutionPayloadEnvelope(blockRootHex: string): Promise<Uint8Array | null>;

produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody>;
produceBlock(blockAttributes: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}): Promise<{
Expand Down
13 changes: 13 additions & 0 deletions packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,19 @@ export class ReqRespBeaconNode extends ReqResp {
);
}

if (ForkSeq[fork] >= ForkSeq.gloas) {
protocolsAtFork.push(
[
protocols.ExecutionPayloadEnvelopesByRoot(fork, this.config),
this.getHandler(ReqRespMethod.ExecutionPayloadEnvelopesByRoot),
],
[
protocols.ExecutionPayloadEnvelopesByRange(fork, this.config),
this.getHandler(ReqRespMethod.ExecutionPayloadEnvelopesByRange),
]
);
}

return protocolsAtFork;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {ChainConfig} from "@lodestar/config";
import {GENESIS_SLOT} from "@lodestar/params";
import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
import {computeEpochAtSlot} from "@lodestar/state-transition";
import {gloas} from "@lodestar/types";
import {IBeaconChain} from "../../../chain/index.js";
import {IBeaconDb} from "../../../db/index.js";

export async function* onExecutionPayloadEnvelopesByRange(
request: gloas.ExecutionPayloadEnvelopesByRangeRequest,
chain: IBeaconChain,
db: IBeaconDb
): AsyncIterable<ResponseOutgoing> {
const {startSlot, count} = validateExecutionPayloadEnvelopesByRangeRequest(chain.config, request);
Comment thread
ensi321 marked this conversation as resolved.
Resolved
const endSlot = startSlot + count;

Comment thread
wemeetagain marked this conversation as resolved.
Resolved
const finalized = db.executionPayloadEnvelopeArchive;
const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot;

// Finalized range of envelopes
if (startSlot <= finalizedSlot) {
for await (const {key, value: envelopeBytes} of finalized.binaryEntriesStream({
gte: startSlot,
lt: endSlot,
})) {
const slot = finalized.decodeKey(key);
yield {
data: envelopeBytes,
boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(slot)),
};
}
}

// Non-finalized range of envelopes
if (endSlot > finalizedSlot) {
const headBlock = chain.forkChoice.getHead();
const headRoot = headBlock.blockRoot;
const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);

// Iterate head chain with ascending block numbers
for (let i = headChain.length - 1; i >= 0; i--) {
const block = headChain[i];

if (block.slot >= startSlot && block.slot < endSlot) {
const envelopeBytes = await chain.getSerializedExecutionPayloadEnvelope(block.blockRoot);
if (envelopeBytes) {
yield {
data: envelopeBytes,
boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(block.slot)),
};
}
// In ePBS, missing envelopes are valid (payload withholding) — skip silently
Comment thread
ensi321 marked this conversation as resolved.
Resolved
} else if (block.slot >= endSlot) {
break;
}
}
}
}

export function validateExecutionPayloadEnvelopesByRangeRequest(
config: ChainConfig,
request: gloas.ExecutionPayloadEnvelopesByRangeRequest
): gloas.ExecutionPayloadEnvelopesByRangeRequest {
const {startSlot} = request;
let {count} = request;

if (count < 1) {
throw new ResponseError(RespStatus.INVALID_REQUEST, "count < 1");
}
if (startSlot < GENESIS_SLOT) {
throw new ResponseError(RespStatus.INVALID_REQUEST, "startSlot < genesis");
}

if (count > config.MAX_REQUEST_BLOCKS_DENEB) {
count = config.MAX_REQUEST_BLOCKS_DENEB;
Comment thread
twoeths marked this conversation as resolved.
Resolved
}

return {startSlot, count};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {ResponseOutgoing} from "@lodestar/reqresp";
import {computeEpochAtSlot} from "@lodestar/state-transition";
import {toRootHex} from "@lodestar/utils";
import {IBeaconChain} from "../../../chain/index.js";
import {ExecutionPayloadEnvelopesByRootRequest} from "../../../util/types.js";

export async function* onExecutionPayloadEnvelopesByRoot(
requestBody: ExecutionPayloadEnvelopesByRootRequest,
chain: IBeaconChain
): AsyncIterable<ResponseOutgoing> {
// Spec: [max(GLOAS_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]
const currentEpoch = chain.clock.currentEpoch;
const minimumRequestEpoch = Math.max(
currentEpoch - chain.config.MIN_EPOCHS_FOR_BLOCK_REQUESTS,
chain.config.GLOAS_FORK_EPOCH
);

for (const root of requestBody) {
const rootHex = toRootHex(root);
const block = chain.forkChoice.getBlockHexDefaultStatus(rootHex);

if (!block) {
continue;
}

const requestedEpoch = computeEpochAtSlot(block.slot);
if (requestedEpoch < minimumRequestEpoch) {
continue;
}

const envelopeBytes = await chain.getSerializedExecutionPayloadEnvelope(rootHex);
if (envelopeBytes) {
yield {
data: envelopeBytes,
boundary: chain.config.getForkBoundaryAtEpoch(requestedEpoch),
};
}
}
}
12 changes: 12 additions & 0 deletions packages/beacon-node/src/network/reqresp/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
BeaconBlocksByRootRequestType,
BlobSidecarsByRootRequestType,
DataColumnSidecarsByRootRequestType,
ExecutionPayloadEnvelopesByRootRequestType,
} from "../../../util/types.js";
import {GetReqRespHandlerFn, ReqRespMethod} from "../types.js";
import {onBeaconBlocksByRange} from "./beaconBlocksByRange.js";
Expand All @@ -14,6 +15,8 @@ import {onBlobSidecarsByRange} from "./blobSidecarsByRange.js";
import {onBlobSidecarsByRoot} from "./blobSidecarsByRoot.js";
import {onDataColumnSidecarsByRange} from "./dataColumnSidecarsByRange.js";
import {onDataColumnSidecarsByRoot} from "./dataColumnSidecarsByRoot.js";
import {onExecutionPayloadEnvelopesByRange} from "./executionPayloadEnvelopesByRange.js";
import {onExecutionPayloadEnvelopesByRoot} from "./executionPayloadEnvelopesByRoot.js";
import {onLightClientBootstrap} from "./lightClientBootstrap.js";
import {onLightClientFinalityUpdate} from "./lightClientFinalityUpdate.js";
import {onLightClientOptimisticUpdate} from "./lightClientOptimisticUpdate.js";
Expand Down Expand Up @@ -62,6 +65,15 @@ export function getReqRespHandlers({db, chain}: {db: IBeaconDb; chain: IBeaconCh
return onDataColumnSidecarsByRoot(body, chain, db, peerId, peerClient);
},

[ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: (req) => {
const body = ExecutionPayloadEnvelopesByRootRequestType(chain.config).deserialize(req.data);
return onExecutionPayloadEnvelopesByRoot(body, chain);
},
[ReqRespMethod.ExecutionPayloadEnvelopesByRange]: (req) => {
const body = ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest.deserialize(req.data);
return onExecutionPayloadEnvelopesByRange(body, chain, db);
},

[ReqRespMethod.LightClientBootstrap]: (req) => {
const body = ssz.Root.deserialize(req.data);
return onLightClientBootstrap(body, chain);
Expand Down
12 changes: 12 additions & 0 deletions packages/beacon-node/src/network/reqresp/protocols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ export const DataColumnSidecarsByRoot = toProtocol({
contextBytesType: ContextBytesType.ForkDigest,
});

export const ExecutionPayloadEnvelopesByRoot = toProtocol({
method: ReqRespMethod.ExecutionPayloadEnvelopesByRoot,
version: Version.V1,
contextBytesType: ContextBytesType.ForkDigest,
});

export const ExecutionPayloadEnvelopesByRange = toProtocol({
method: ReqRespMethod.ExecutionPayloadEnvelopesByRange,
version: Version.V1,
contextBytesType: ContextBytesType.ForkDigest,
});

export const LightClientBootstrap = toProtocol({
method: ReqRespMethod.LightClientBootstrap,
version: Version.V1,
Expand Down
18 changes: 18 additions & 0 deletions packages/beacon-node/src/network/reqresp/rateLimit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ export const rateLimitQuotas: (fork: ForkName, config: BeaconConfig) => Record<R
req.reduce((total, item) => total + item.columns.length, 0)
),
},
[ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: {
byPeer: {quota: config.MAX_REQUEST_PAYLOADS, quotaTimeMs: 10_000},
getRequestCount: getRequestCountFn(
fork,
config,
ReqRespMethod.ExecutionPayloadEnvelopesByRoot,
(req) => req.length
),
},
[ReqRespMethod.ExecutionPayloadEnvelopesByRange]: {
byPeer: {quota: config.MAX_REQUEST_BLOCKS_DENEB, quotaTimeMs: 10_000},
getRequestCount: getRequestCountFn(
fork,
config,
ReqRespMethod.ExecutionPayloadEnvelopesByRange,
(req) => req.count
),
},
[ReqRespMethod.LightClientBootstrap]: {
// As similar in the nature of `Status` protocol so we use the same rate limits.
byPeer: {quota: 5, quotaTimeMs: 15_000},
Expand Down
2 changes: 2 additions & 0 deletions packages/beacon-node/src/network/reqresp/score.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export function onOutgoingReqRespError(e: RequestError, method: ReqRespMethod):
return PeerAction.LowToleranceError;
case ReqRespMethod.BeaconBlocksByRange:
case ReqRespMethod.BeaconBlocksByRoot:
case ReqRespMethod.ExecutionPayloadEnvelopesByRoot:
case ReqRespMethod.ExecutionPayloadEnvelopesByRange:
Comment thread
ensi321 marked this conversation as resolved.
Resolved
Comment thread
ensi321 marked this conversation as resolved.
Resolved
return PeerAction.MidToleranceError;
default:
return null;
Expand Down
13 changes: 13 additions & 0 deletions packages/beacon-node/src/network/reqresp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
altair,
deneb,
fulu,
gloas,
phase0,
ssz,
sszTypesFor,
Expand All @@ -25,6 +26,8 @@ import {
BlobSidecarsByRootRequestType,
DataColumnSidecarsByRootRequest,
DataColumnSidecarsByRootRequestType,
ExecutionPayloadEnvelopesByRootRequest,
ExecutionPayloadEnvelopesByRootRequestType,
} from "../../util/types.js";

export type ProtocolNoHandler = Omit<Protocol, "handler">;
Expand All @@ -42,6 +45,8 @@ export enum ReqRespMethod {
BlobSidecarsByRoot = "blob_sidecars_by_root",
DataColumnSidecarsByRange = "data_column_sidecars_by_range",
DataColumnSidecarsByRoot = "data_column_sidecars_by_root",
ExecutionPayloadEnvelopesByRoot = "execution_payload_envelopes_by_root",
ExecutionPayloadEnvelopesByRange = "execution_payload_envelopes_by_range",
LightClientBootstrap = "light_client_bootstrap",
LightClientUpdatesByRange = "light_client_updates_by_range",
LightClientFinalityUpdate = "light_client_finality_update",
Expand All @@ -60,6 +65,8 @@ export type RequestBodyByMethod = {
[ReqRespMethod.BlobSidecarsByRoot]: BlobSidecarsByRootRequest;
[ReqRespMethod.DataColumnSidecarsByRange]: fulu.DataColumnSidecarsByRangeRequest;
[ReqRespMethod.DataColumnSidecarsByRoot]: DataColumnSidecarsByRootRequest;
[ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: ExecutionPayloadEnvelopesByRootRequest;
[ReqRespMethod.ExecutionPayloadEnvelopesByRange]: gloas.ExecutionPayloadEnvelopesByRangeRequest;
[ReqRespMethod.LightClientBootstrap]: Root;
[ReqRespMethod.LightClientUpdatesByRange]: altair.LightClientUpdatesByRange;
[ReqRespMethod.LightClientFinalityUpdate]: null;
Expand All @@ -78,6 +85,8 @@ type ResponseBodyByMethod = {
[ReqRespMethod.BlobSidecarsByRoot]: deneb.BlobSidecar;
[ReqRespMethod.DataColumnSidecarsByRange]: fulu.DataColumnSidecar;
[ReqRespMethod.DataColumnSidecarsByRoot]: fulu.DataColumnSidecar;
[ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: gloas.SignedExecutionPayloadEnvelope;
[ReqRespMethod.ExecutionPayloadEnvelopesByRange]: gloas.SignedExecutionPayloadEnvelope;

[ReqRespMethod.LightClientBootstrap]: LightClientBootstrap;
[ReqRespMethod.LightClientUpdatesByRange]: LightClientUpdate;
Expand Down Expand Up @@ -105,6 +114,8 @@ export const requestSszTypeByMethod: (
[ReqRespMethod.BlobSidecarsByRoot]: BlobSidecarsByRootRequestType(fork, config),
[ReqRespMethod.DataColumnSidecarsByRange]: ssz.fulu.DataColumnSidecarsByRangeRequest,
[ReqRespMethod.DataColumnSidecarsByRoot]: DataColumnSidecarsByRootRequestType(config),
[ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: ExecutionPayloadEnvelopesByRootRequestType(config),
[ReqRespMethod.ExecutionPayloadEnvelopesByRange]: ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest,

[ReqRespMethod.LightClientBootstrap]: ssz.Root,
[ReqRespMethod.LightClientUpdatesByRange]: ssz.altair.LightClientUpdatesByRange,
Expand Down Expand Up @@ -137,6 +148,8 @@ export const responseSszTypeByMethod: {[K in ReqRespMethod]: ResponseTypeGetter<
[ReqRespMethod.LightClientFinalityUpdate]: (fork) => sszTypesFor(onlyPostAltairFork(fork)).LightClientFinalityUpdate,
[ReqRespMethod.DataColumnSidecarsByRange]: () => ssz.fulu.DataColumnSidecar,
[ReqRespMethod.DataColumnSidecarsByRoot]: () => ssz.fulu.DataColumnSidecar,
[ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: () => ssz.gloas.SignedExecutionPayloadEnvelope,
[ReqRespMethod.ExecutionPayloadEnvelopesByRange]: () => ssz.gloas.SignedExecutionPayloadEnvelope,
[ReqRespMethod.LightClientOptimisticUpdate]: (fork) =>
sszTypesFor(onlyPostAltairFork(fork)).LightClientOptimisticUpdate,
};
Expand Down
6 changes: 6 additions & 0 deletions packages/beacon-node/src/util/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ export type BlobSidecarsByRootRequest = ValueOf<ReturnType<typeof BlobSidecarsBy
export const DataColumnSidecarsByRootRequestType = (config: BeaconConfig) =>
new ListCompositeType(ssz.fulu.DataColumnsByRootIdentifier, config.MAX_REQUEST_BLOCKS_DENEB);
export type DataColumnSidecarsByRootRequest = ValueOf<ReturnType<typeof DataColumnSidecarsByRootRequestType>>;

export const ExecutionPayloadEnvelopesByRootRequestType = (config: BeaconConfig) =>
new ListCompositeType(ssz.Root, config.MAX_REQUEST_PAYLOADS);
export type ExecutionPayloadEnvelopesByRootRequest = ValueOf<
ReturnType<typeof ExecutionPayloadEnvelopesByRootRequestType>
>;
5 changes: 5 additions & 0 deletions packages/types/src/gloas/sszTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,8 @@ export const DataColumnSidecar = new ContainerType(
);

export const DataColumnSidecars = new ListCompositeType(DataColumnSidecar, NUMBER_OF_COLUMNS);

export const ExecutionPayloadEnvelopesByRangeRequest = new ContainerType(
{startSlot: Slot, count: UintNum64},
{typeName: "ExecutionPayloadEnvelopesByRangeRequest", jsonCase: "eth2"}
);
2 changes: 2 additions & 0 deletions packages/types/src/gloas/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ export type BeaconState = ValueOf<typeof ssz.BeaconState>;

export type DataColumnSidecar = ValueOf<typeof ssz.DataColumnSidecar>;
export type DataColumnSidecars = ValueOf<typeof ssz.DataColumnSidecars>;

export type ExecutionPayloadEnvelopesByRangeRequest = ValueOf<typeof ssz.ExecutionPayloadEnvelopesByRangeRequest>;
Loading