@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
83 lines • 4.34 kB
JavaScript
import { computeEpochAtSlot } from "@lodestar/state-transition";
import { BLOB_AND_PROOF_V2_RPC_BYTES } from "../execution/engine/types.js";
import { callInNextEventLoop } from "../util/eventLoop.js";
import { DataColumnEngineResult, getBlobSidecarsFromExecution, getDataColumnSidecarsFromExecution, } from "../util/execution.js";
import { isBlockInputBlobs } from "./blocks/blockInput/index.js";
import { PayloadEnvelopeInput } from "./blocks/payloadEnvelopeInput/index.js";
/**
* Tracks getBlobsV2 calls to the execution engine to avoid duplicate and multiple in-flight calls
*/
export class GetBlobsTracker {
logger;
executionEngine;
emitter;
metrics;
config;
activeReconstructions = new Set();
// Preallocate buffers for getBlobsV2 RPC calls
// See https://github.com/ChainSafe/lodestar/pull/8282 for context
blobsAndProofsBuffers = [];
constructor(init) {
this.logger = init.logger;
this.executionEngine = init.executionEngine;
this.emitter = init.emitter;
this.metrics = init.metrics;
this.config = init.config;
}
triggerGetBlobs(input) {
if (this.activeReconstructions.has(input.blockRootHex)) {
return;
}
if (!(input instanceof PayloadEnvelopeInput) && isBlockInputBlobs(input)) {
// there is not preallocation for blob sidecars like there is for columns sidecars so no need to
// store the index for the preallocated buffers
this.activeReconstructions.add(input.blockRootHex);
callInNextEventLoop(() => {
const logCtx = { slot: input.slot, root: input.blockRootHex };
this.logger.verbose("Trigger getBlobsV1 for block", logCtx);
getBlobSidecarsFromExecution(this.config, this.executionEngine, this.metrics, this.emitter, input).finally(() => {
this.logger.verbose("Completed getBlobsV1 for block", logCtx);
this.activeReconstructions.delete(input.blockRootHex);
});
});
return;
}
let freeIndex = this.blobsAndProofsBuffers.findIndex(({ inUse }) => !inUse);
if (freeIndex === -1) {
freeIndex = this.blobsAndProofsBuffers.length;
this.blobsAndProofsBuffers[freeIndex] = { inUse: false, buffers: [] };
}
const maxBlobs = this.config.getMaxBlobsPerBlock(computeEpochAtSlot(input.slot));
// double check that there is enough pre-allocated space (blob schedule may have changed since the last use)
const timer = this.metrics?.peerDas.getBlobsV2PreAllocationTime.startTimer();
for (let i = 0; i < maxBlobs; i++) {
if (this.blobsAndProofsBuffers[freeIndex].buffers[i] === undefined) {
this.blobsAndProofsBuffers[freeIndex].buffers[i] = new Uint8Array(BLOB_AND_PROOF_V2_RPC_BYTES);
}
}
timer?.();
// We don't care about the outcome of this call,
// just that it has been triggered for this block root.
this.activeReconstructions.add(input.blockRootHex);
this.blobsAndProofsBuffers[freeIndex].inUse = true;
callInNextEventLoop(() => {
const logCtx = { slot: input.slot, root: input.blockRootHex };
this.logger.verbose("Trigger getBlobsV2 for block", logCtx);
getDataColumnSidecarsFromExecution(this.config, this.executionEngine, this.emitter, input, this.metrics, this.blobsAndProofsBuffers[freeIndex].buffers)
.then((result) => {
this.logger.debug("getBlobsV2 result for block", { ...logCtx, result });
this.metrics?.dataColumns.dataColumnEngineResult.inc({ result });
})
.catch((error) => {
this.logger.debug("Error during getBlobsV2 for block", logCtx, error);
this.metrics?.dataColumns.dataColumnEngineResult.inc({ result: DataColumnEngineResult.Failed });
})
.finally(() => {
this.logger.verbose("Completed getBlobsV2 for block", logCtx);
this.activeReconstructions.delete(input.blockRootHex);
this.blobsAndProofsBuffers[freeIndex].inUse = false;
});
});
}
}
//# sourceMappingURL=GetBlobsTracker.js.map