Skip to content

Commit

Permalink
fix validator handling
Browse files Browse the repository at this point in the history
  • Loading branch information
g11tech committed Nov 6, 2023
1 parent 89e4bd1 commit fcc2aec
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 197 deletions.
21 changes: 2 additions & 19 deletions packages/state-transition/src/signatureSets/proposer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {DOMAIN_BEACON_PROPOSER, DOMAIN_BLOB_SIDECAR} from "@lodestar/params";
import {allForks, isBlindedBeaconBlock, isBlindedBlobSidecar, ssz} from "@lodestar/types";
import {DOMAIN_BEACON_PROPOSER} from "@lodestar/params";
import {allForks, isBlindedBeaconBlock} from "@lodestar/types";
import {computeSigningRoot} from "../util/index.js";
import {ISignatureSet, SignatureSetType, verifySignatureSet} from "../util/signatureSets.js";
import {CachedBeaconStateAllForks} from "../types.js";
Expand Down Expand Up @@ -30,20 +30,3 @@ export function getBlockProposerSignatureSet(
signature: signedBlock.signature,
};
}

export function getBlobProposerSignatureSet(
state: CachedBeaconStateAllForks,
signedBlob: allForks.FullOrBlindedSignedBlobSidecar
): ISignatureSet {
const {config, epochCtx} = state;
const domain = config.getDomain(state.slot, DOMAIN_BLOB_SIDECAR, signedBlob.message.slot);

const blockType = isBlindedBlobSidecar(signedBlob.message) ? ssz.deneb.BlindedBlobSidecar : ssz.deneb.BlobSidecar;

return {
type: SignatureSetType.single,
pubkey: epochCtx.index2pubkey[signedBlob.message.proposerIndex],
signingRoot: computeSigningRoot(blockType, signedBlob.message, domain),
signature: signedBlob.signature,
};
}
86 changes: 11 additions & 75 deletions packages/state-transition/src/util/blindedBlock.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,9 @@
import {ChainForkConfig} from "@lodestar/config";
import {ForkSeq} from "@lodestar/params";
import {
allForks,
phase0,
Root,
deneb,
ssz,
isBlindedBeaconBlock,
isBlindedBlobSidecar,
isSignedBlindedBlockContents,
isExecutionPayloadAndBlobsBundle,
} from "@lodestar/types";
import {allForks, phase0, Root, deneb, isBlindedBeaconBlock, isExecutionPayloadAndBlobsBundle} from "@lodestar/types";

import {executionPayloadToPayloadHeader} from "./execution.js";

type ParsedSignedBlindedBlockOrContents = {
signedBlindedBlock: allForks.SignedBlindedBeaconBlock;
signedBlindedBlobSidecars: deneb.SignedBlindedBlobSidecars | null;
};

export function blindedOrFullBlockHashTreeRoot(
config: ChainForkConfig,
blindedOrFull: allForks.FullOrBlindedBeaconBlock
Expand All @@ -30,17 +15,6 @@ export function blindedOrFullBlockHashTreeRoot(
config.getForkTypes(blindedOrFull.slot).BeaconBlock.hashTreeRoot(blindedOrFull);
}

export function blindedOrFullBlobSidecarHashTreeRoot(
config: ChainForkConfig,
blindedOrFull: allForks.FullOrBlindedBlobSidecar
): Root {
return isBlindedBlobSidecar(blindedOrFull)
? // Blinded
config.getBlobsForkTypes(blindedOrFull.slot).BlindedBlobSidecar.hashTreeRoot(blindedOrFull)
: // Full
config.getBlobsForkTypes(blindedOrFull.slot).BlobSidecar.hashTreeRoot(blindedOrFull);
}

export function blindedOrFullBlockToHeader(
config: ChainForkConfig,
blindedOrFull: allForks.FullOrBlindedBeaconBlock
Expand Down Expand Up @@ -70,13 +44,6 @@ export function beaconBlockToBlinded(
return blindedBlock;
}

export function blobSidecarsToBlinded(blobSidecars: deneb.BlobSidecars): deneb.BlindedBlobSidecars {
return blobSidecars.map((blobSidecar) => {
const blobRoot = ssz.deneb.Blob.hashTreeRoot(blobSidecar.blob);
return {...blobSidecar, blobRoot} as deneb.BlindedBlobSidecar;
});
}

export function signedBlindedBlockToFull(
signedBlindedBlock: allForks.SignedBlindedBeaconBlock,
executionPayload: allForks.ExecutionPayload | null
Expand All @@ -100,33 +67,6 @@ export function signedBlindedBlockToFull(
return signedBlock;
}

export function signedBlindedBlobSidecarsToFull(
signedBlindedBlobSidecars: deneb.SignedBlindedBlobSidecars,
blobs: deneb.Blobs
): deneb.SignedBlobSidecars {
const signedBlobSidecars = signedBlindedBlobSidecars.map((signedBlindedBlobSidecar, index) => {
const signedBlobSidecar = {
...signedBlindedBlobSidecar,
message: {...signedBlindedBlobSidecar.message, blob: blobs[index]},
};
delete (signedBlobSidecar.message as {blobRoot?: deneb.BlindedBlob}).blobRoot;
return signedBlobSidecar;
});
return signedBlobSidecars;
}

export function parseSignedBlindedBlockOrContents(
signedBlindedBlockOrContents: allForks.SignedBlindedBeaconBlockOrContents
): ParsedSignedBlindedBlockOrContents {
if (isSignedBlindedBlockContents(signedBlindedBlockOrContents)) {
const signedBlindedBlock = signedBlindedBlockOrContents.signedBlindedBlock;
const signedBlindedBlobSidecars = signedBlindedBlockOrContents.signedBlindedBlobSidecars;
return {signedBlindedBlock, signedBlindedBlobSidecars};
} else {
return {signedBlindedBlock: signedBlindedBlockOrContents, signedBlindedBlobSidecars: null};
}
}

export function parseExecutionPayloadAndBlobsBundle(
data: allForks.ExecutionPayload | allForks.ExecutionPayloadAndBlobsBundle
): {executionPayload: allForks.ExecutionPayload; blobsBundle: deneb.BlobsBundle | null} {
Expand All @@ -141,27 +81,23 @@ export function parseExecutionPayloadAndBlobsBundle(
}

export function reconstructFullBlockOrContents(
{signedBlindedBlock, signedBlindedBlobSidecars}: ParsedSignedBlindedBlockOrContents,
{executionPayload, blobs}: {executionPayload: allForks.ExecutionPayload | null; blobs: deneb.Blobs | null}
signedBlindedBlock: allForks.SignedBlindedBeaconBlock,
{
executionPayload,
contents,
}: {
executionPayload: allForks.ExecutionPayload | null;
contents: {blobs: deneb.Blobs; kzgProofs: deneb.KZGProofs} | null;
}
): allForks.SignedBeaconBlockOrContents {
const signedBlock = signedBlindedBlockToFull(signedBlindedBlock, executionPayload);

if (signedBlindedBlobSidecars !== null) {
if (contents !== null) {
if (executionPayload === null) {
throw Error("Missing locally produced executionPayload for deneb+ publishBlindedBlock");
}

if (blobs === null) {
throw Error("Missing blobs from the local execution cache");
}
if (blobs.length !== signedBlindedBlobSidecars.length) {
throw Error(
`Length mismatch signedBlindedBlobSidecars=${signedBlindedBlobSidecars.length} blobs=${blobs.length}`
);
}
const signedBlobSidecars = signedBlindedBlobSidecarsToFull(signedBlindedBlobSidecars, blobs);

return {signedBlock, signedBlobSidecars} as allForks.SignedBeaconBlockOrContents;
return {signedBlock, ...contents} as allForks.SignedBeaconBlockOrContents;
} else {
return signedBlock as allForks.SignedBeaconBlockOrContents;
}
Expand Down
96 changes: 33 additions & 63 deletions packages/validator/src/services/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import {
ProducedBlockSource,
deneb,
isBlockContents,
isBlindedBlockContents,
} from "@lodestar/types";
import {ChainForkConfig} from "@lodestar/config";
import {ForkPreBlobs, ForkBlobs, ForkSeq} from "@lodestar/params";
import {ForkPreBlobs, ForkBlobs, ForkSeq, ForkExecution} from "@lodestar/params";
import {extendError, prettyBytes} from "@lodestar/utils";
import {Api, ApiError, routes} from "@lodestar/api";
import {IClock, LoggerVc} from "../util/index.js";
Expand All @@ -28,31 +27,27 @@ const MAX_DECIMAL_FACTOR = BigInt("100000");
// The following combination of blocks and blobs can be produced
// i) a full block pre deneb
// ii) a full block and full blobs post deneb
// iii) a blinded block pre deneb as a result of beacon/execution race
// iv) a blinded block + blinded blobs as a result of beacon/execution race
// iii) a blinded block post bellatrix
type FullOrBlindedBlockWithContents =
| {
version: ForkPreBlobs;
block: allForks.BeaconBlock;
blobs: null;
contents: null;
executionPayloadBlinded: false;
}
| {
version: ForkBlobs;
block: allForks.BeaconBlock;
blobs: deneb.BlobSidecars;
contents: {
kzgProofs: deneb.KZGProofs;
blobs: deneb.Blobs;
};
executionPayloadBlinded: false;
}
| {
version: ForkPreBlobs;
block: allForks.BlindedBeaconBlock;
blobs: null;
executionPayloadBlinded: true;
}
| {
version: ForkBlobs;
version: ForkExecution;
block: allForks.BlindedBeaconBlock;
blobs: deneb.BlindedBlobSidecars;
contents: null;
executionPayloadBlinded: true;
};

Expand Down Expand Up @@ -144,21 +139,8 @@ export class BlockProposingService {
this.logger.debug("Produced block", {...debugLogCtx, ...blockContents.debugLogCtx});
this.metrics?.blocksProduced.inc();

const signedBlockPromise = this.validatorStore.signBlock(pubkey, blockContents.block, slot);
const signedBlobPromises =
blockContents.blobs !== null
? blockContents.blobs.map((blob) => this.validatorStore.signBlob(pubkey, blob, slot))
: undefined;
let signedBlock: allForks.FullOrBlindedSignedBeaconBlock,
signedBlobs: allForks.FullOrBlindedSignedBlobSidecar[] | undefined;
if (signedBlobPromises !== undefined) {
[signedBlock, ...signedBlobs] = await Promise.all([signedBlockPromise, ...signedBlobPromises]);
} else {
signedBlock = await signedBlockPromise;
signedBlobs = undefined;
}

await this.publishBlockWrapper(signedBlock, signedBlobs).catch((e: Error) => {
const signedBlock = await this.validatorStore.signBlock(pubkey, blockContents.block, slot);
await this.publishBlockWrapper(signedBlock, blockContents.contents).catch((e: Error) => {
this.metrics?.blockProposingErrors.inc({error: "publish"});
throw extendError(e, "Failed to publish block");
});
Expand All @@ -172,23 +154,21 @@ export class BlockProposingService {

private publishBlockWrapper = async (
signedBlock: allForks.FullOrBlindedSignedBeaconBlock,
signedBlobSidecars?: allForks.FullOrBlindedSignedBlobSidecar[]
contents: {kzgProofs: deneb.KZGProofs; blobs: deneb.Blobs} | null
): Promise<void> => {
if (signedBlobSidecars === undefined) {
ApiError.assert(
isBlindedBeaconBlock(signedBlock.message)
? await this.api.beacon.publishBlindedBlock(signedBlock as allForks.SignedBlindedBeaconBlock)
: await this.api.beacon.publishBlockV2(signedBlock as allForks.SignedBeaconBlock)
);
if (isBlindedBeaconBlock(signedBlock.message)) {
if (contents !== null) {
this.logger.warn(
"Ignoring contents while publishing blinded block - publishing beacon should assemble it from its local cache or builder"
);
}
ApiError.assert(await this.api.beacon.publishBlindedBlock(signedBlock));
} else {
ApiError.assert(
isBlindedBeaconBlock(signedBlock.message)
? await this.api.beacon.publishBlindedBlock({
signedBlindedBlock: signedBlock,
signedBlindedBlobSidecars: signedBlobSidecars,
} as allForks.SignedBlindedBlockContents)
: await this.api.beacon.publishBlockV2({signedBlock, signedBlobSidecars} as allForks.SignedBlockContents)
);
if (contents === null) {
ApiError.assert(await this.api.beacon.publishBlockV2(signedBlock));
} else {
ApiError.assert(await this.api.beacon.publishBlockV2({...contents, signedBlock}));
}
}
};

Expand Down Expand Up @@ -255,36 +235,26 @@ function parseProduceBlockResponse(
debugLogCtx: Record<string, string | boolean | undefined>
): FullOrBlindedBlockWithContents & DebugLogCtx {
if (response.executionPayloadBlinded) {
if (isBlindedBlockContents(response.data)) {
return {
block: response.data.blindedBlock,
blobs: response.data.blindedBlobSidecars,
version: response.version,
executionPayloadBlinded: true,
debugLogCtx,
} as FullOrBlindedBlockWithContents & DebugLogCtx;
} else {
return {
block: response.data,
blobs: null,
version: response.version,
executionPayloadBlinded: true,
debugLogCtx,
} as FullOrBlindedBlockWithContents & DebugLogCtx;
}
return {
block: response.data,
contents: null,
version: response.version,
executionPayloadBlinded: true,
debugLogCtx,
} as FullOrBlindedBlockWithContents & DebugLogCtx;
} else {
if (isBlockContents(response.data)) {
return {
block: response.data.block,
blobs: response.data.blobSidecars,
contents: {blobs: response.data.blobs, kzgProofs: response.data.kzgProofs},
version: response.version,
executionPayloadBlinded: false,
debugLogCtx,
} as FullOrBlindedBlockWithContents & DebugLogCtx;
} else {
return {
block: response.data,
blobs: null,
contents: null,
version: response.version,
executionPayloadBlinded: false,
debugLogCtx,
Expand Down
33 changes: 0 additions & 33 deletions packages/validator/src/services/validatorStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
computeDomain,
ZERO_HASH,
blindedOrFullBlockHashTreeRoot,
blindedOrFullBlobSidecarHashTreeRoot,
} from "@lodestar/state-transition";
import {BeaconConfig} from "@lodestar/config";
import {
Expand All @@ -20,7 +19,6 @@ import {
DOMAIN_SYNC_COMMITTEE,
DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF,
DOMAIN_APPLICATION_BUILDER,
DOMAIN_BLOB_SIDECAR,
} from "@lodestar/params";
import {
allForks,
Expand Down Expand Up @@ -385,37 +383,6 @@ export class ValidatorStore {
} as allForks.FullOrBlindedSignedBeaconBlock;
}

async signBlob(
pubkey: BLSPubkey,
blindedOrFull: allForks.FullOrBlindedBlobSidecar,
currentSlot: Slot
): Promise<allForks.FullOrBlindedSignedBlobSidecar> {
// Make sure the block slot is not higher than the current slot to avoid potential attacks.
if (blindedOrFull.slot > currentSlot) {
throw Error(`Not signing block with slot ${blindedOrFull.slot} greater than current slot ${currentSlot}`);
}

// Duties are filtered before-hard by doppelganger-safe, this assert should never throw
this.assertDoppelgangerSafe(pubkey);

const signingSlot = blindedOrFull.slot;
const domain = this.config.getDomain(signingSlot, DOMAIN_BLOB_SIDECAR);
const blobRoot = blindedOrFullBlobSidecarHashTreeRoot(this.config, blindedOrFull);
// Don't use `computeSigningRoot()` here to compute the objectRoot in typesafe function blindedOrFullBlockHashTreeRoot()
const signingRoot = ssz.phase0.SigningData.hashTreeRoot({objectRoot: blobRoot, domain});

// Slashing protection is not required as blobs are binded to blocks which are already protected
const signableMessage: SignableMessage = {
type: SignableMessageType.BLOB,
data: blindedOrFull,
};

return {
message: blindedOrFull,
signature: await this.getSignature(pubkey, signingRoot, signingSlot, signableMessage),
} as allForks.FullOrBlindedSignedBlobSidecar;
}

async signRandao(pubkey: BLSPubkey, slot: Slot): Promise<BLSSignature> {
const signingSlot = slot;
const domain = this.config.getDomain(slot, DOMAIN_RANDAO);
Expand Down
Loading

0 comments on commit fcc2aec

Please sign in to comment.