mirror of
https://github.com/ChainSafe/lodestar.git
synced 2026-01-09 15:48:08 -05:00
Merge branch 'unstable' into nc/epbs-p2p
This commit is contained in:
@@ -28,6 +28,7 @@ export async function verifyBlocksSignatures(
|
||||
): Promise<{verifySignaturesTime: number}> {
|
||||
const isValidPromises: Promise<boolean>[] = [];
|
||||
const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000);
|
||||
const currentSyncCommitteeIndexed = preState0.epochCtx.currentSyncCommitteeIndexed;
|
||||
|
||||
// Verifies signatures after running state transition, so all SyncCommittee signed roots are known at this point.
|
||||
// We must ensure block.slot <= state.slot before running getAllBlockSignatureSets().
|
||||
@@ -41,9 +42,16 @@ export async function verifyBlocksSignatures(
|
||||
: //
|
||||
// Verify signatures per block to track which block is invalid
|
||||
bls.verifySignatureSets(
|
||||
getBlockSignatureSets(config, index2pubkey, preState0, block, indexedAttestationsByBlock[i], {
|
||||
skipProposerSignature: opts.validProposerSignature,
|
||||
})
|
||||
getBlockSignatureSets(
|
||||
config,
|
||||
index2pubkey,
|
||||
currentSyncCommitteeIndexed,
|
||||
block,
|
||||
indexedAttestationsByBlock[i],
|
||||
{
|
||||
skipProposerSignature: opts.validProposerSignature,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// getBlockSignatureSets() takes 45ms in benchmarks for 2022Q2 mainnet blocks (100 sigs). When syncing a 32 blocks
|
||||
|
||||
@@ -265,6 +265,18 @@ async function validateAggregateAndProof(
|
||||
});
|
||||
}
|
||||
|
||||
// Same race-condition check as above for seen aggregators
|
||||
if (
|
||||
!skipValidationKnownAttesters &&
|
||||
chain.seenAggregatedAttestations.isKnown(targetEpoch, attIndex, attDataRootHex, aggregationBits)
|
||||
) {
|
||||
throw new AttestationError(GossipAction.IGNORE, {
|
||||
code: AttestationErrorCode.ATTESTERS_ALREADY_KNOWN,
|
||||
targetEpoch,
|
||||
aggregateRoot: attDataRootHex,
|
||||
});
|
||||
}
|
||||
|
||||
chain.seenAggregators.add(targetEpoch, aggregatorIndex);
|
||||
chain.seenAggregatedAttestations.add(
|
||||
targetEpoch,
|
||||
|
||||
@@ -51,7 +51,12 @@ export async function validateAttesterSlashing(
|
||||
});
|
||||
}
|
||||
|
||||
const signatureSets = getAttesterSlashingSignatureSets(chain.config, chain.index2pubkey, state, attesterSlashing);
|
||||
const signatureSets = getAttesterSlashingSignatureSets(
|
||||
chain.config,
|
||||
chain.index2pubkey,
|
||||
state.slot,
|
||||
attesterSlashing
|
||||
);
|
||||
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
|
||||
throw new AttesterSlashingError(GossipAction.REJECT, {
|
||||
code: AttesterSlashingErrorCode.INVALID,
|
||||
|
||||
@@ -155,7 +155,7 @@ export async function validateGossipBlock(
|
||||
|
||||
// [REJECT] The proposer signature, signed_beacon_block.signature, is valid with respect to the proposer_index pubkey.
|
||||
if (!chain.seenBlockInputCache.isVerifiedProposerSignature(blockSlot, blockRoot, signedBlock.signature)) {
|
||||
const signatureSet = getBlockProposerSignatureSet(chain.config, chain.index2pubkey, blockState, signedBlock);
|
||||
const signatureSet = getBlockProposerSignatureSet(chain.config, chain.index2pubkey, signedBlock);
|
||||
// Don't batch so verification is not delayed
|
||||
if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: true}))) {
|
||||
throw new BlockGossipError(GossipAction.REJECT, {
|
||||
|
||||
@@ -44,7 +44,12 @@ async function validateProposerSlashing(
|
||||
});
|
||||
}
|
||||
|
||||
const signatureSets = getProposerSlashingSignatureSets(chain.config, chain.index2pubkey, state, proposerSlashing);
|
||||
const signatureSets = getProposerSlashingSignatureSets(
|
||||
chain.config,
|
||||
chain.index2pubkey,
|
||||
state.slot,
|
||||
proposerSlashing
|
||||
);
|
||||
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
|
||||
throw new ProposerSlashingError(GossipAction.REJECT, {
|
||||
code: ProposerSlashingErrorCode.INVALID,
|
||||
|
||||
@@ -59,7 +59,7 @@ async function validateVoluntaryExit(
|
||||
});
|
||||
}
|
||||
|
||||
const signatureSet = getVoluntaryExitSignatureSet(chain.config, chain.index2pubkey, state, voluntaryExit);
|
||||
const signatureSet = getVoluntaryExitSignatureSet(chain.config, chain.index2pubkey, state.slot, voluntaryExit);
|
||||
if (!(await chain.bls.verifySignatureSets([signatureSet], {batchable: true, priority: prioritizeBls}))) {
|
||||
throw new VoluntaryExitError(GossipAction.REJECT, {
|
||||
code: VoluntaryExitErrorCode.INVALID_SIGNATURE,
|
||||
|
||||
@@ -55,8 +55,7 @@ export async function verifyBlockProposerSignature(
|
||||
if (blocks.length === 1 && blocks[0].message.slot === GENESIS_SLOT) return;
|
||||
const signatures = blocks.reduce((sigs: ISignatureSet[], block) => {
|
||||
// genesis block doesn't have valid signature
|
||||
if (block.message.slot !== GENESIS_SLOT)
|
||||
sigs.push(getBlockProposerSignatureSet(config, index2pubkey, state, block));
|
||||
if (block.message.slot !== GENESIS_SLOT) sigs.push(getBlockProposerSignatureSet(config, index2pubkey, block));
|
||||
return sigs;
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ export function isValidIndexedAttestation(
|
||||
}
|
||||
|
||||
if (verifySignature) {
|
||||
return verifySignatureSet(getIndexedAttestationSignatureSet(config, index2pubkey, state, indexedAttestation));
|
||||
return verifySignatureSet(getIndexedAttestationSignatureSet(config, index2pubkey, state.slot, indexedAttestation));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -38,7 +38,9 @@ export function isValidIndexedAttestationBigint(
|
||||
}
|
||||
|
||||
if (verifySignature) {
|
||||
return verifySignatureSet(getIndexedAttestationBigintSignatureSet(config, index2pubkey, state, indexedAttestation));
|
||||
return verifySignatureSet(
|
||||
getIndexedAttestationBigintSignatureSet(config, index2pubkey, state.slot, indexedAttestation)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ export function processAttestationsAltair(
|
||||
const sigSet = getAttestationWithIndicesSignatureSet(
|
||||
state.config,
|
||||
epochCtx.index2pubkey,
|
||||
state,
|
||||
state.slot,
|
||||
attestation,
|
||||
attestingIndices
|
||||
);
|
||||
|
||||
@@ -80,7 +80,7 @@ export function assertValidProposerSlashing(
|
||||
const signatureSets = getProposerSlashingSignatureSets(
|
||||
state.config,
|
||||
state.epochCtx.index2pubkey,
|
||||
state,
|
||||
state.slot,
|
||||
proposerSlashing
|
||||
);
|
||||
for (let i = 0; i < signatureSets.length; i++) {
|
||||
|
||||
@@ -12,12 +12,12 @@ import {getRandaoMix} from "../util/index.js";
|
||||
* PERF: Fixed work independent of block contents.
|
||||
*/
|
||||
export function processRandao(state: CachedBeaconStateAllForks, block: BeaconBlock, verifySignature = true): void {
|
||||
const {epochCtx} = state;
|
||||
const {epochCtx, config} = state;
|
||||
const epoch = epochCtx.epoch;
|
||||
const randaoReveal = block.body.randaoReveal;
|
||||
|
||||
// verify RANDAO reveal
|
||||
if (verifySignature && !verifyRandaoSignature(state.config, epochCtx.index2pubkey, state, block)) {
|
||||
if (verifySignature && !verifyRandaoSignature(config, epochCtx.index2pubkey, block)) {
|
||||
throw new Error("RANDAO reveal is an invalid signature");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_SYNC_COMMITTEE, SYNC_COMMITTEE_SIZE} from "@lodestar/params";
|
||||
import {altair, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
import {SyncCommitteeCache} from "../cache/syncCommitteeCache.js";
|
||||
import {G2_POINT_AT_INFINITY} from "../constants/index.js";
|
||||
import {CachedBeaconStateAllForks} from "../types.js";
|
||||
import {
|
||||
@@ -28,7 +29,7 @@ export function processSyncAggregate(
|
||||
const signatureSet = getSyncCommitteeSignatureSet(
|
||||
state.config,
|
||||
state.epochCtx.index2pubkey,
|
||||
state,
|
||||
state.epochCtx.currentSyncCommitteeIndexed,
|
||||
block,
|
||||
participantIndices
|
||||
);
|
||||
@@ -73,7 +74,7 @@ export function processSyncAggregate(
|
||||
export function getSyncCommitteeSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
currentSyncCommitteeIndexed: SyncCommitteeCache,
|
||||
block: altair.BeaconBlock,
|
||||
/** Optional parameter to prevent computing it twice */
|
||||
participantIndices?: number[]
|
||||
@@ -101,7 +102,7 @@ export function getSyncCommitteeSignatureSet(
|
||||
const rootSigned = block.parentRoot;
|
||||
|
||||
if (!participantIndices) {
|
||||
const committeeIndices = state.epochCtx.currentSyncCommitteeIndexed.validatorIndices;
|
||||
const committeeIndices = currentSyncCommitteeIndexed.validatorIndices;
|
||||
participantIndices = syncAggregate.syncCommitteeBits.intersectValues(committeeIndices);
|
||||
}
|
||||
|
||||
@@ -115,7 +116,9 @@ export function getSyncCommitteeSignatureSet(
|
||||
throw Error("Empty sync committee signature is not infinity");
|
||||
}
|
||||
|
||||
const domain = config.getDomain(state.slot, DOMAIN_SYNC_COMMITTEE, previousSlot);
|
||||
// the getDomain() api requires the state slot as 1st param, however it's the same to block.slot in state-transition
|
||||
// and the same epoch when we verify blocks in batch in beacon-node. So we can safely use block.slot here.
|
||||
const domain = config.getDomain(block.slot, DOMAIN_SYNC_COMMITTEE, previousSlot);
|
||||
|
||||
return {
|
||||
type: SignatureSetType.aggregate,
|
||||
|
||||
@@ -76,7 +76,7 @@ export function getVoluntaryExitValidity(
|
||||
|
||||
if (
|
||||
verifySignature &&
|
||||
!verifyVoluntaryExitSignature(state.config, epochCtx.index2pubkey, state, signedVoluntaryExit)
|
||||
!verifyVoluntaryExitSignature(state.config, epochCtx.index2pubkey, state.slot, signedVoluntaryExit)
|
||||
) {
|
||||
return VoluntaryExitValidity.invalidSignature;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params";
|
||||
import {AttesterSlashing, IndexedAttestationBigint, SignedBeaconBlock, ssz} from "@lodestar/types";
|
||||
import {AttesterSlashing, IndexedAttestationBigint, SignedBeaconBlock, Slot, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
import {CachedBeaconStateAllForks} from "../types.js";
|
||||
import {ISignatureSet, SignatureSetType, computeSigningRoot, computeStartSlotAtEpoch} from "../util/index.js";
|
||||
|
||||
/** Get signature sets from all AttesterSlashing objects in a block */
|
||||
export function getAttesterSlashingsSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock
|
||||
): ISignatureSet[] {
|
||||
// the getDomain() api requires the state slot as 1st param, however it's the same to block.slot in state-transition
|
||||
// and the same epoch when we verify blocks in batch in beacon-node. So we can safely use block.slot here.
|
||||
const blockSlot = signedBlock.message.slot;
|
||||
return signedBlock.message.body.attesterSlashings.flatMap((attesterSlashing) =>
|
||||
getAttesterSlashingSignatureSets(config, index2pubkey, state, attesterSlashing)
|
||||
getAttesterSlashingSignatureSets(config, index2pubkey, blockSlot, attesterSlashing)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,22 +22,22 @@ export function getAttesterSlashingsSignatureSets(
|
||||
export function getAttesterSlashingSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
stateSlot: Slot,
|
||||
attesterSlashing: AttesterSlashing
|
||||
): ISignatureSet[] {
|
||||
return [attesterSlashing.attestation1, attesterSlashing.attestation2].map((attestation) =>
|
||||
getIndexedAttestationBigintSignatureSet(config, index2pubkey, state, attestation)
|
||||
getIndexedAttestationBigintSignatureSet(config, index2pubkey, stateSlot, attestation)
|
||||
);
|
||||
}
|
||||
|
||||
export function getIndexedAttestationBigintSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
stateSlot: Slot,
|
||||
indexedAttestation: IndexedAttestationBigint
|
||||
): ISignatureSet {
|
||||
const slot = computeStartSlotAtEpoch(Number(indexedAttestation.data.target.epoch as bigint));
|
||||
const domain = config.getDomain(state.slot, DOMAIN_BEACON_ATTESTER, slot);
|
||||
const messageSlot = computeStartSlotAtEpoch(Number(indexedAttestation.data.target.epoch as bigint));
|
||||
const domain = config.getDomain(stateSlot, DOMAIN_BEACON_ATTESTER, messageSlot);
|
||||
|
||||
return {
|
||||
type: SignatureSetType.aggregate,
|
||||
|
||||
@@ -3,7 +3,7 @@ import {ForkSeq} from "@lodestar/params";
|
||||
import {IndexedAttestation, SignedBeaconBlock, altair, capella} from "@lodestar/types";
|
||||
import {getSyncCommitteeSignatureSet} from "../block/processSyncCommittee.js";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
import {CachedBeaconStateAllForks, CachedBeaconStateAltair} from "../types.js";
|
||||
import {SyncCommitteeCache} from "../cache/syncCommitteeCache.js";
|
||||
import {ISignatureSet} from "../util/index.js";
|
||||
import {getAttesterSlashingsSignatureSets} from "./attesterSlashings.js";
|
||||
import {getBlsToExecutionChangeSignatureSets} from "./blsToExecutionChange.js";
|
||||
@@ -31,7 +31,7 @@ export * from "./voluntaryExits.js";
|
||||
export function getBlockSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
currentSyncCommitteeIndexed: SyncCommitteeCache,
|
||||
signedBlock: SignedBeaconBlock,
|
||||
indexedAttestations: IndexedAttestation[],
|
||||
opts?: {
|
||||
@@ -40,18 +40,18 @@ export function getBlockSignatureSets(
|
||||
}
|
||||
): ISignatureSet[] {
|
||||
// fork based validations
|
||||
const fork = state.config.getForkSeq(signedBlock.message.slot);
|
||||
const fork = config.getForkSeq(signedBlock.message.slot);
|
||||
|
||||
const signatureSets = [
|
||||
getRandaoRevealSignatureSet(config, index2pubkey, state, signedBlock.message),
|
||||
...getProposerSlashingsSignatureSets(config, index2pubkey, state, signedBlock),
|
||||
...getAttesterSlashingsSignatureSets(config, index2pubkey, state, signedBlock),
|
||||
...getAttestationsSignatureSets(config, index2pubkey, state, signedBlock, indexedAttestations),
|
||||
...getVoluntaryExitsSignatureSets(config, index2pubkey, state, signedBlock),
|
||||
getRandaoRevealSignatureSet(config, index2pubkey, signedBlock.message),
|
||||
...getProposerSlashingsSignatureSets(config, index2pubkey, signedBlock),
|
||||
...getAttesterSlashingsSignatureSets(config, index2pubkey, signedBlock),
|
||||
...getAttestationsSignatureSets(config, index2pubkey, signedBlock, indexedAttestations),
|
||||
...getVoluntaryExitsSignatureSets(config, index2pubkey, signedBlock),
|
||||
];
|
||||
|
||||
if (!opts?.skipProposerSignature) {
|
||||
signatureSets.push(getBlockProposerSignatureSet(config, index2pubkey, state, signedBlock));
|
||||
signatureSets.push(getBlockProposerSignatureSet(config, index2pubkey, signedBlock));
|
||||
}
|
||||
|
||||
// Only after altair fork, validate tSyncCommitteeSignature
|
||||
@@ -59,7 +59,7 @@ export function getBlockSignatureSets(
|
||||
const syncCommitteeSignatureSet = getSyncCommitteeSignatureSet(
|
||||
config,
|
||||
index2pubkey,
|
||||
state as CachedBeaconStateAltair,
|
||||
currentSyncCommitteeIndexed,
|
||||
(signedBlock as altair.SignedBeaconBlock).message
|
||||
);
|
||||
// There may be no participants in this syncCommitteeSignature, so it must not be validated
|
||||
@@ -71,7 +71,7 @@ export function getBlockSignatureSets(
|
||||
// only after capella fork
|
||||
if (fork >= ForkSeq.capella) {
|
||||
const blsToExecutionChangeSignatureSets = getBlsToExecutionChangeSignatureSets(
|
||||
state.config,
|
||||
config,
|
||||
signedBlock as capella.SignedBeaconBlock
|
||||
);
|
||||
if (blsToExecutionChangeSignatureSets.length > 0) {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params";
|
||||
import {IndexedAttestation, SignedBeaconBlock, phase0, ssz} from "@lodestar/types";
|
||||
import {IndexedAttestation, SignedBeaconBlock, Slot, phase0, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
import {CachedBeaconStateAllForks} from "../types.js";
|
||||
import {
|
||||
ISignatureSet,
|
||||
computeSigningRoot,
|
||||
@@ -12,11 +11,11 @@ import {
|
||||
|
||||
export function getAttestationDataSigningRoot(
|
||||
config: BeaconConfig,
|
||||
state: CachedBeaconStateAllForks,
|
||||
stateSlot: Slot,
|
||||
data: phase0.AttestationData
|
||||
): Uint8Array {
|
||||
const slot = computeStartSlotAtEpoch(data.target.epoch);
|
||||
const domain = config.getDomain(state.slot, DOMAIN_BEACON_ATTESTER, slot);
|
||||
const messageSlot = computeStartSlotAtEpoch(data.target.epoch);
|
||||
const domain = config.getDomain(stateSlot, DOMAIN_BEACON_ATTESTER, messageSlot);
|
||||
|
||||
return computeSigningRoot(ssz.phase0.AttestationData, data, domain);
|
||||
}
|
||||
@@ -24,13 +23,13 @@ export function getAttestationDataSigningRoot(
|
||||
export function getAttestationWithIndicesSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
stateSlot: Slot,
|
||||
attestation: Pick<phase0.Attestation, "data" | "signature">,
|
||||
attestingIndices: number[]
|
||||
): ISignatureSet {
|
||||
return createAggregateSignatureSetFromComponents(
|
||||
attestingIndices.map((i) => index2pubkey[i]),
|
||||
getAttestationDataSigningRoot(config, state, attestation.data),
|
||||
getAttestationDataSigningRoot(config, stateSlot, attestation.data),
|
||||
attestation.signature
|
||||
);
|
||||
}
|
||||
@@ -38,13 +37,13 @@ export function getAttestationWithIndicesSignatureSet(
|
||||
export function getIndexedAttestationSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
stateSlot: Slot,
|
||||
indexedAttestation: IndexedAttestation
|
||||
): ISignatureSet {
|
||||
return getAttestationWithIndicesSignatureSet(
|
||||
config,
|
||||
index2pubkey,
|
||||
state,
|
||||
stateSlot,
|
||||
indexedAttestation,
|
||||
indexedAttestation.attestingIndices
|
||||
);
|
||||
@@ -53,7 +52,6 @@ export function getIndexedAttestationSignatureSet(
|
||||
export function getAttestationsSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock,
|
||||
indexedAttestations: IndexedAttestation[]
|
||||
): ISignatureSet[] {
|
||||
@@ -62,7 +60,10 @@ export function getAttestationsSignatureSets(
|
||||
`Indexed attestations length mismatch: got ${indexedAttestations.length}, expected ${signedBlock.message.body.attestations.length}`
|
||||
);
|
||||
}
|
||||
// the getDomain() api requires the state slot as 1st param, however it's the same to block.slot in state-transition
|
||||
// and the same epoch when we verify blocks in batch in beacon-node. So we can safely use block.slot here.
|
||||
const blockSlot = signedBlock.message.slot;
|
||||
return indexedAttestations.map((indexedAttestation) =>
|
||||
getIndexedAttestationSignatureSet(config, index2pubkey, state, indexedAttestation)
|
||||
getIndexedAttestationSignatureSet(config, index2pubkey, blockSlot, indexedAttestation)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,20 +9,21 @@ import {ISignatureSet, SignatureSetType, verifySignatureSet} from "../util/signa
|
||||
export function verifyProposerSignature(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock
|
||||
): boolean {
|
||||
const signatureSet = getBlockProposerSignatureSet(config, index2pubkey, state, signedBlock);
|
||||
const signatureSet = getBlockProposerSignatureSet(config, index2pubkey, signedBlock);
|
||||
return verifySignatureSet(signatureSet);
|
||||
}
|
||||
|
||||
export function getBlockProposerSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock
|
||||
): ISignatureSet {
|
||||
const domain = config.getDomain(state.slot, DOMAIN_BEACON_PROPOSER, signedBlock.message.slot);
|
||||
// the getDomain() api requires the state slot as 1st param, however it's the same to block.slot in state-transition
|
||||
// and the same epoch when we verify blocks in batch in beacon-node. So we can safely use block.slot here.
|
||||
const blockSlot = signedBlock.message.slot;
|
||||
const domain = config.getDomain(blockSlot, DOMAIN_BEACON_PROPOSER, blockSlot);
|
||||
|
||||
const blockType = isBlindedBeaconBlock(signedBlock.message)
|
||||
? config.getPostBellatrixForkTypes(signedBlock.message.slot).BlindedBeaconBlock
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_BEACON_PROPOSER} from "@lodestar/params";
|
||||
import {SignedBeaconBlock, phase0, ssz} from "@lodestar/types";
|
||||
import {SignedBeaconBlock, Slot, phase0, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
import {CachedBeaconStateAllForks} from "../types.js";
|
||||
import {ISignatureSet, SignatureSetType, computeSigningRoot} from "../util/index.js";
|
||||
|
||||
/**
|
||||
@@ -11,7 +10,7 @@ import {ISignatureSet, SignatureSetType, computeSigningRoot} from "../util/index
|
||||
export function getProposerSlashingSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
stateSlot: Slot,
|
||||
proposerSlashing: phase0.ProposerSlashing
|
||||
): ISignatureSet[] {
|
||||
const pubkey = index2pubkey[proposerSlashing.signedHeader1.message.proposerIndex];
|
||||
@@ -19,7 +18,7 @@ export function getProposerSlashingSignatureSets(
|
||||
// In state transition, ProposerSlashing headers are only partially validated. Their slot could be higher than the
|
||||
// clock and the slashing would still be valid. Must use bigint variants to hash correctly to all possible values
|
||||
return [proposerSlashing.signedHeader1, proposerSlashing.signedHeader2].map((signedHeader): ISignatureSet => {
|
||||
const domain = config.getDomain(state.slot, DOMAIN_BEACON_PROPOSER, Number(signedHeader.message.slot as bigint));
|
||||
const domain = config.getDomain(stateSlot, DOMAIN_BEACON_PROPOSER, Number(signedHeader.message.slot as bigint));
|
||||
|
||||
return {
|
||||
type: SignatureSetType.single,
|
||||
@@ -33,10 +32,12 @@ export function getProposerSlashingSignatureSets(
|
||||
export function getProposerSlashingsSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock
|
||||
): ISignatureSet[] {
|
||||
// the getDomain() api requires the state slot as 1st param, however it's the same to block.slot in state-transition
|
||||
// and the same epoch when we verify blocks in batch in beacon-node. So we can safely use block.slot here.
|
||||
const blockSlot = signedBlock.message.slot;
|
||||
return signedBlock.message.body.proposerSlashings.flatMap((proposerSlashing) =>
|
||||
getProposerSlashingSignatureSets(config, index2pubkey, state, proposerSlashing)
|
||||
getProposerSlashingSignatureSets(config, index2pubkey, blockSlot, proposerSlashing)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_RANDAO} from "@lodestar/params";
|
||||
import {BeaconBlock, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
import {CachedBeaconStateAllForks} from "../types.js";
|
||||
import {
|
||||
ISignatureSet,
|
||||
SignatureSetType,
|
||||
@@ -14,10 +13,9 @@ import {
|
||||
export function verifyRandaoSignature(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
block: BeaconBlock
|
||||
): boolean {
|
||||
return verifySignatureSet(getRandaoRevealSignatureSet(config, index2pubkey, state, block));
|
||||
return verifySignatureSet(getRandaoRevealSignatureSet(config, index2pubkey, block));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -26,12 +24,13 @@ export function verifyRandaoSignature(
|
||||
export function getRandaoRevealSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
block: BeaconBlock
|
||||
): ISignatureSet {
|
||||
// should not get epoch from epochCtx
|
||||
const epoch = computeEpochAtSlot(block.slot);
|
||||
const domain = config.getDomain(state.slot, DOMAIN_RANDAO, block.slot);
|
||||
// the getDomain() api requires the state slot as 1st param, however it's the same to block.slot in state-transition
|
||||
// and the same epoch when we verify blocks in batch in beacon-node. So we can safely use block.slot here.
|
||||
const domain = config.getDomain(block.slot, DOMAIN_RANDAO, block.slot);
|
||||
|
||||
return {
|
||||
type: SignatureSetType.single,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {SignedBeaconBlock, phase0, ssz} from "@lodestar/types";
|
||||
import {SignedBeaconBlock, Slot, phase0, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
import {CachedBeaconStateAllForks} from "../types.js";
|
||||
import {
|
||||
ISignatureSet,
|
||||
SignatureSetType,
|
||||
@@ -13,10 +12,10 @@ import {
|
||||
export function verifyVoluntaryExitSignature(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
stateSlot: Slot,
|
||||
signedVoluntaryExit: phase0.SignedVoluntaryExit
|
||||
): boolean {
|
||||
return verifySignatureSet(getVoluntaryExitSignatureSet(config, index2pubkey, state, signedVoluntaryExit));
|
||||
return verifySignatureSet(getVoluntaryExitSignatureSet(config, index2pubkey, stateSlot, signedVoluntaryExit));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,11 +24,11 @@ export function verifyVoluntaryExitSignature(
|
||||
export function getVoluntaryExitSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
stateSlot: Slot,
|
||||
signedVoluntaryExit: phase0.SignedVoluntaryExit
|
||||
): ISignatureSet {
|
||||
const slot = computeStartSlotAtEpoch(signedVoluntaryExit.message.epoch);
|
||||
const domain = config.getDomainForVoluntaryExit(state.slot, slot);
|
||||
const messageSlot = computeStartSlotAtEpoch(signedVoluntaryExit.message.epoch);
|
||||
const domain = config.getDomainForVoluntaryExit(stateSlot, messageSlot);
|
||||
|
||||
return {
|
||||
type: SignatureSetType.single,
|
||||
@@ -42,10 +41,12 @@ export function getVoluntaryExitSignatureSet(
|
||||
export function getVoluntaryExitsSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock
|
||||
): ISignatureSet[] {
|
||||
// the getDomain() api requires the state slot as 1st param, however it's the same to block.slot in state-transition
|
||||
// and the same epoch when we verify blocks in batch in beacon-node. So we can safely use block.slot here.
|
||||
const blockSlot = signedBlock.message.slot;
|
||||
return signedBlock.message.body.voluntaryExits.map((voluntaryExit) =>
|
||||
getVoluntaryExitSignatureSet(config, index2pubkey, state, voluntaryExit)
|
||||
getVoluntaryExitSignatureSet(config, index2pubkey, blockSlot, voluntaryExit)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -111,10 +111,7 @@ export function stateTransition(
|
||||
postState = processSlotsWithTransientCache(postState, blockSlot, options, {metrics, validatorMonitor});
|
||||
|
||||
// Verify proposer signature only
|
||||
if (
|
||||
verifyProposer &&
|
||||
!verifyProposerSignature(postState.config, postState.epochCtx.index2pubkey, postState, signedBlock)
|
||||
) {
|
||||
if (verifyProposer && !verifyProposerSignature(postState.config, postState.epochCtx.index2pubkey, signedBlock)) {
|
||||
throw new Error("Invalid block signature");
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ describe("signatureSets", () => {
|
||||
const signatureSets = getBlockSignatureSets(
|
||||
state.config,
|
||||
state.epochCtx.index2pubkey,
|
||||
state,
|
||||
state.epochCtx.currentSyncCommitteeIndexed,
|
||||
signedBlock,
|
||||
indexedAttestations
|
||||
);
|
||||
|
||||
@@ -469,20 +469,33 @@ export class AttestationDutiesService {
|
||||
|
||||
const res = await this.api.validator.submitBeaconCommitteeSelections({selections: partialSelections});
|
||||
|
||||
const combinedSelections = res.value();
|
||||
this.logger.debug("Received combined beacon committee selection proofs", {epoch, count: combinedSelections.length});
|
||||
const combinedSelections = new Map<ValidatorIndex, routes.validator.BeaconCommitteeSelection>();
|
||||
for (const selection of res.value()) {
|
||||
combinedSelections.set(selection.validatorIndex, selection);
|
||||
}
|
||||
this.logger.debug("Received combined beacon committee selection proofs", {epoch, count: combinedSelections.size});
|
||||
|
||||
for (const dutyAndProof of duties) {
|
||||
const {slot, validatorIndex, committeeIndex, committeeLength} = dutyAndProof.duty;
|
||||
const logCtxValidator = {slot, index: committeeIndex, validatorIndex};
|
||||
|
||||
const combinedSelection = combinedSelections.find((s) => s.validatorIndex === validatorIndex && s.slot === slot);
|
||||
const combinedSelection = combinedSelections.get(validatorIndex);
|
||||
|
||||
if (!combinedSelection) {
|
||||
this.logger.debug("Did not receive combined beacon committee selection proof", logCtxValidator);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (combinedSelection.slot !== slot) {
|
||||
this.logger.debug("Received combined beacon committee selection proof for different slot", {
|
||||
expected: slot,
|
||||
actual: combinedSelection.slot,
|
||||
index: committeeIndex,
|
||||
validatorIndex,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const isAggregator = isAggregatorFromCommitteeLength(committeeLength, combinedSelection.selectionProof);
|
||||
|
||||
if (isAggregator) {
|
||||
|
||||
Reference in New Issue
Block a user