@lodestar/beacon-node 1.40.0-dev.1c71f4299a → 1.40.0-dev.2ae7375100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +7 -7
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +10 -2
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +9 -8
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/ColumnReconstructionTracker.d.ts +2 -0
- package/lib/chain/ColumnReconstructionTracker.d.ts.map +1 -1
- package/lib/chain/ColumnReconstructionTracker.js +7 -3
- package/lib/chain/ColumnReconstructionTracker.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +1 -1
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSignatures.d.ts +2 -2
- package/lib/chain/blocks/verifyBlocksSignatures.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSignatures.js +2 -2
- package/lib/chain/blocks/verifyBlocksSignatures.js.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts +3 -1
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +5 -3
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/bls/multithread/jobItem.d.ts +2 -2
- package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
- package/lib/chain/bls/multithread/jobItem.js +2 -2
- package/lib/chain/bls/multithread/jobItem.js.map +1 -1
- package/lib/chain/bls/singleThread.d.ts +4 -2
- package/lib/chain/bls/singleThread.d.ts.map +1 -1
- package/lib/chain/bls/singleThread.js +4 -2
- package/lib/chain/bls/singleThread.js.map +1 -1
- package/lib/chain/bls/utils.d.ts +2 -2
- package/lib/chain/bls/utils.d.ts.map +1 -1
- package/lib/chain/bls/utils.js +9 -6
- package/lib/chain/bls/utils.js.map +1 -1
- package/lib/chain/chain.d.ts +8 -3
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +36 -18
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/attestationError.d.ts +14 -1
- package/lib/chain/errors/attestationError.d.ts.map +1 -1
- package/lib/chain/errors/attestationError.js +8 -0
- package/lib/chain/errors/attestationError.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +48 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -0
- package/lib/chain/errors/executionPayloadBid.js +15 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +48 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -0
- package/lib/chain/errors/executionPayloadEnvelope.js +16 -0
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -0
- package/lib/chain/errors/index.d.ts +3 -0
- package/lib/chain/errors/index.d.ts.map +1 -1
- package/lib/chain/errors/index.js +3 -0
- package/lib/chain/errors/index.js.map +1 -1
- package/lib/chain/errors/payloadAttestation.d.ts +34 -0
- package/lib/chain/errors/payloadAttestation.d.ts.map +1 -0
- package/lib/chain/errors/payloadAttestation.js +13 -0
- package/lib/chain/errors/payloadAttestation.js.map +1 -0
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +18 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +7 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.d.ts +21 -0
- package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -0
- package/lib/chain/opPools/executionPayloadBidPool.js +57 -0
- package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -0
- package/lib/chain/opPools/index.d.ts +2 -0
- package/lib/chain/opPools/index.d.ts.map +1 -1
- package/lib/chain/opPools/index.js +2 -0
- package/lib/chain/opPools/index.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts +24 -0
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -0
- package/lib/chain/opPools/payloadAttestationPool.js +109 -0
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -0
- package/lib/chain/prepareNextSlot.js +6 -4
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +5 -3
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +3 -8
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +1 -1
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts +1 -2
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +2 -16
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +5 -7
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +7 -17
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +3 -1
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +3 -1
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/seenCache/seenAttesters.d.ts +5 -0
- package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
- package/lib/chain/seenCache/seenAttesters.js +5 -0
- package/lib/chain/seenCache/seenAttesters.js.map +1 -1
- package/lib/chain/seenCache/seenExecutionPayloadBids.d.ts +12 -0
- package/lib/chain/seenCache/seenExecutionPayloadBids.d.ts.map +1 -0
- package/lib/chain/seenCache/seenExecutionPayloadBids.js +30 -0
- package/lib/chain/seenCache/seenExecutionPayloadBids.js.map +1 -0
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.d.ts +15 -0
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.d.ts.map +1 -0
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.js +28 -0
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.js.map +1 -0
- package/lib/chain/seenCache/seenGossipBlockInput.js +1 -1
- package/lib/chain/validation/aggregateAndProof.js +35 -14
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.d.ts +2 -2
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +27 -8
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +1 -1
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/blobSidecar.d.ts.map +1 -1
- package/lib/chain/validation/blobSidecar.js +3 -3
- package/lib/chain/validation/blobSidecar.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +5 -4
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.js +3 -3
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts +5 -0
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -0
- package/lib/chain/validation/executionPayloadBid.js +104 -0
- package/lib/chain/validation/executionPayloadBid.js.map +1 -0
- package/lib/chain/validation/executionPayloadEnvelope.d.ts +5 -0
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -0
- package/lib/chain/validation/executionPayloadEnvelope.js +89 -0
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -0
- package/lib/chain/validation/payloadAttestationMessage.d.ts +9 -0
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -0
- package/lib/chain/validation/payloadAttestationMessage.js +72 -0
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -0
- package/lib/chain/validation/proposerSlashing.js +1 -1
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts +2 -3
- package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts.map +1 -1
- package/lib/chain/validation/signatureSets/aggregateAndProof.js +8 -3
- package/lib/chain/validation/signatureSets/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/signatureSets/contributionAndProof.d.ts +2 -2
- package/lib/chain/validation/signatureSets/contributionAndProof.d.ts.map +1 -1
- package/lib/chain/validation/signatureSets/contributionAndProof.js +3 -3
- package/lib/chain/validation/signatureSets/contributionAndProof.js.map +1 -1
- package/lib/chain/validation/signatureSets/selectionProof.d.ts +2 -3
- package/lib/chain/validation/signatureSets/selectionProof.d.ts.map +1 -1
- package/lib/chain/validation/signatureSets/selectionProof.js +8 -3
- package/lib/chain/validation/signatureSets/selectionProof.js.map +1 -1
- package/lib/chain/validation/signatureSets/syncCommittee.d.ts +2 -2
- package/lib/chain/validation/signatureSets/syncCommittee.d.ts.map +1 -1
- package/lib/chain/validation/signatureSets/syncCommittee.js +3 -3
- package/lib/chain/validation/signatureSets/syncCommittee.js.map +1 -1
- package/lib/chain/validation/signatureSets/syncCommitteeContribution.d.ts +1 -2
- package/lib/chain/validation/signatureSets/syncCommitteeContribution.d.ts.map +1 -1
- package/lib/chain/validation/signatureSets/syncCommitteeContribution.js +2 -2
- package/lib/chain/validation/signatureSets/syncCommitteeContribution.js.map +1 -1
- package/lib/chain/validation/signatureSets/syncCommitteeSelectionProof.d.ts +2 -2
- package/lib/chain/validation/signatureSets/syncCommitteeSelectionProof.d.ts.map +1 -1
- package/lib/chain/validation/signatureSets/syncCommitteeSelectionProof.js +3 -3
- package/lib/chain/validation/signatureSets/syncCommitteeSelectionProof.js.map +1 -1
- package/lib/chain/validation/syncCommittee.js +1 -1
- package/lib/chain/validation/syncCommittee.js.map +1 -1
- package/lib/chain/validation/syncCommitteeContributionAndProof.d.ts.map +1 -1
- package/lib/chain/validation/syncCommitteeContributionAndProof.js +3 -5
- package/lib/chain/validation/syncCommitteeContributionAndProof.js.map +1 -1
- package/lib/chain/validation/voluntaryExit.js +1 -1
- package/lib/chain/validation/voluntaryExit.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +7 -4
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/db/repositories/checkpointState.js +0 -1
- package/lib/db/repositories/checkpointState.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +20 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +40 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/interface.d.ts +20 -2
- package/lib/network/gossip/interface.d.ts.map +1 -1
- package/lib/network/gossip/interface.js +3 -0
- package/lib/network/gossip/interface.js.map +1 -1
- package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
- package/lib/network/gossip/scoringParameters.js +38 -2
- package/lib/network/gossip/scoringParameters.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +77 -1
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +20 -0
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +34 -0
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
- package/lib/network/processor/gossipQueues/index.js +16 -0
- package/lib/network/processor/gossipQueues/index.js.map +1 -1
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +3 -0
- package/lib/network/processor/index.js.map +1 -1
- package/lib/sync/backfill/backfill.js +2 -2
- package/lib/sync/backfill/backfill.js.map +1 -1
- package/lib/sync/backfill/verify.d.ts +1 -2
- package/lib/sync/backfill/verify.d.ts.map +1 -1
- package/lib/sync/backfill/verify.js +2 -2
- package/lib/sync/backfill/verify.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +9 -7
- package/src/api/impl/lodestar/index.ts +12 -3
- package/src/api/impl/validator/index.ts +12 -11
- package/src/chain/ColumnReconstructionTracker.ts +8 -4
- package/src/chain/blocks/verifyBlock.ts +0 -1
- package/src/chain/blocks/verifyBlocksSignatures.ts +4 -12
- package/src/chain/bls/multithread/index.ts +7 -4
- package/src/chain/bls/multithread/jobItem.ts +7 -3
- package/src/chain/bls/singleThread.ts +5 -3
- package/src/chain/bls/utils.ts +15 -7
- package/src/chain/chain.ts +39 -26
- package/src/chain/errors/attestationError.ts +11 -1
- package/src/chain/errors/executionPayloadBid.ts +35 -0
- package/src/chain/errors/executionPayloadEnvelope.ts +34 -0
- package/src/chain/errors/index.ts +3 -0
- package/src/chain/errors/payloadAttestation.ts +25 -0
- package/src/chain/forkChoice/index.ts +19 -0
- package/src/chain/interface.ts +16 -1
- package/src/chain/opPools/executionPayloadBidPool.ts +77 -0
- package/src/chain/opPools/index.ts +2 -0
- package/src/chain/opPools/payloadAttestationPool.ts +157 -0
- package/src/chain/prepareNextSlot.ts +6 -6
- package/src/chain/produceBlock/produceBlockBody.ts +7 -5
- package/src/chain/regen/interface.ts +2 -12
- package/src/chain/regen/queued.ts +3 -23
- package/src/chain/regen/regen.ts +10 -25
- package/src/chain/seenCache/index.ts +3 -1
- package/src/chain/seenCache/seenAttesters.ts +5 -0
- package/src/chain/seenCache/seenExecutionPayloadBids.ts +35 -0
- package/src/chain/seenCache/seenExecutionPayloadEnvelope.ts +34 -0
- package/src/chain/seenCache/seenGossipBlockInput.ts +1 -1
- package/src/chain/validation/aggregateAndProof.ts +36 -14
- package/src/chain/validation/attestation.ts +33 -16
- package/src/chain/validation/attesterSlashing.ts +1 -6
- package/src/chain/validation/blobSidecar.ts +2 -7
- package/src/chain/validation/block.ts +5 -4
- package/src/chain/validation/dataColumnSidecar.ts +2 -7
- package/src/chain/validation/executionPayloadBid.ts +141 -0
- package/src/chain/validation/executionPayloadEnvelope.ts +122 -0
- package/src/chain/validation/payloadAttestationMessage.ts +109 -0
- package/src/chain/validation/proposerSlashing.ts +1 -6
- package/src/chain/validation/signatureSets/aggregateAndProof.ts +9 -14
- package/src/chain/validation/signatureSets/contributionAndProof.ts +2 -4
- package/src/chain/validation/signatureSets/selectionProof.ts +9 -9
- package/src/chain/validation/signatureSets/syncCommittee.ts +2 -4
- package/src/chain/validation/signatureSets/syncCommitteeContribution.ts +2 -3
- package/src/chain/validation/signatureSets/syncCommitteeSelectionProof.ts +2 -4
- package/src/chain/validation/syncCommittee.ts +1 -1
- package/src/chain/validation/syncCommitteeContributionAndProof.ts +3 -5
- package/src/chain/validation/voluntaryExit.ts +1 -1
- package/src/chain/validatorMonitor.ts +10 -5
- package/src/db/repositories/checkpointState.ts +1 -1
- package/src/metrics/metrics/lodestar.ts +40 -0
- package/src/network/gossip/interface.ts +17 -0
- package/src/network/gossip/scoringParameters.ts +44 -2
- package/src/network/gossip/topic.ts +21 -0
- package/src/network/processor/gossipHandlers.ts +48 -0
- package/src/network/processor/gossipQueues/index.ts +16 -0
- package/src/network/processor/index.ts +3 -0
- package/src/sync/backfill/backfill.ts +2 -2
- package/src/sync/backfill/verify.ts +2 -3
|
@@ -80,9 +80,8 @@ export class PrepareNextSlotScheduler {
|
|
|
80
80
|
await sleep(this.config.getSlotComponentDurationMs(PREPARE_NEXT_SLOT_BPS), this.signal);
|
|
81
81
|
|
|
82
82
|
// calling updateHead() here before we produce a block to reduce reorg possibility
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
);
|
|
83
|
+
const headBlock = this.chain.recomputeForkChoiceHead(ForkchoiceCaller.prepareNextSlot);
|
|
84
|
+
const {slot: headSlot, blockRoot: headRoot} = headBlock;
|
|
86
85
|
|
|
87
86
|
// PS: previously this was comparing slots, but that gave no leway on the skipped
|
|
88
87
|
// slots on epoch bounday. Making it more fluid.
|
|
@@ -112,7 +111,7 @@ export class PrepareNextSlotScheduler {
|
|
|
112
111
|
// Pre Bellatrix: we only do precompute state transition for the last slot of epoch
|
|
113
112
|
// For Bellatrix, we always do the `processSlots()` to prepare payload for the next slot
|
|
114
113
|
const prepareState = await this.chain.regen.getBlockSlotState(
|
|
115
|
-
|
|
114
|
+
headBlock,
|
|
116
115
|
prepareSlot,
|
|
117
116
|
// the slot 0 of next epoch will likely use this Previous Root Checkpoint state for state transition so we transfer cache here
|
|
118
117
|
// the resulting state with cache will be cached in Checkpoint State Cache which is used for the upcoming block processing
|
|
@@ -129,7 +128,8 @@ export class PrepareNextSlotScheduler {
|
|
|
129
128
|
|
|
130
129
|
if (feeRecipient) {
|
|
131
130
|
// If we are proposing next slot, we need to predict if we can proposer-boost-reorg or not
|
|
132
|
-
const
|
|
131
|
+
const proposerHead = this.chain.predictProposerHead(clockSlot);
|
|
132
|
+
const {slot: proposerHeadSlot, blockRoot: proposerHeadRoot} = proposerHead;
|
|
133
133
|
|
|
134
134
|
// If we predict we can reorg, update prepareState with proposer head block
|
|
135
135
|
if (proposerHeadRoot !== headRoot || proposerHeadSlot !== headSlot) {
|
|
@@ -141,7 +141,7 @@ export class PrepareNextSlotScheduler {
|
|
|
141
141
|
});
|
|
142
142
|
this.metrics?.weakHeadDetected.inc();
|
|
143
143
|
updatedPrepareState = (await this.chain.regen.getBlockSlotState(
|
|
144
|
-
|
|
144
|
+
proposerHead,
|
|
145
145
|
prepareSlot,
|
|
146
146
|
// only transfer cache if epoch transition because that's the state we will use to stateTransition() the 1st block of epoch
|
|
147
147
|
{dontTransferCache: !isEpochTransition},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {getSafeExecutionBlockHash} from "@lodestar/fork-choice";
|
|
2
|
+
import {ProtoBlock, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
|
|
3
3
|
import {
|
|
4
4
|
ForkName,
|
|
5
5
|
ForkPostBellatrix,
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
electra,
|
|
44
44
|
fulu,
|
|
45
45
|
} from "@lodestar/types";
|
|
46
|
-
import {Logger, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
46
|
+
import {Logger, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
47
47
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
48
48
|
import {numToQuantity} from "../../execution/engine/utils.js";
|
|
49
49
|
import {
|
|
@@ -86,7 +86,7 @@ export type BlockAttributes = {
|
|
|
86
86
|
randaoReveal: BLSSignature;
|
|
87
87
|
graffiti: Bytes32;
|
|
88
88
|
slot: Slot;
|
|
89
|
-
|
|
89
|
+
parentBlock: ProtoBlock;
|
|
90
90
|
feeRecipient?: string;
|
|
91
91
|
};
|
|
92
92
|
|
|
@@ -155,13 +155,14 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
155
155
|
const {
|
|
156
156
|
slot: blockSlot,
|
|
157
157
|
feeRecipient: requestedFeeRecipient,
|
|
158
|
-
|
|
158
|
+
parentBlock,
|
|
159
159
|
proposerIndex,
|
|
160
160
|
proposerPubKey,
|
|
161
161
|
commonBlockBodyPromise,
|
|
162
162
|
} = blockAttr;
|
|
163
163
|
let executionPayloadValue: Wei;
|
|
164
164
|
let blockBody: AssembledBodyType<T>;
|
|
165
|
+
const parentBlockRoot = fromHex(parentBlock.blockRoot);
|
|
165
166
|
// even though shouldOverrideBuilder is relevant for the engine response, for simplicity of typing
|
|
166
167
|
// we just return it undefined for the builder which anyway doesn't get consumed downstream
|
|
167
168
|
let shouldOverrideBuilder: boolean | undefined;
|
|
@@ -637,7 +638,7 @@ export async function produceCommonBlockBody<T extends BlockType>(
|
|
|
637
638
|
this: BeaconChain,
|
|
638
639
|
blockType: T,
|
|
639
640
|
currentState: CachedBeaconStateAllForks,
|
|
640
|
-
{randaoReveal, graffiti, slot,
|
|
641
|
+
{randaoReveal, graffiti, slot, parentBlock}: BlockAttributes
|
|
641
642
|
): Promise<CommonBlockBody> {
|
|
642
643
|
const stepsMetrics =
|
|
643
644
|
blockType === BlockType.Full
|
|
@@ -691,6 +692,7 @@ export async function produceCommonBlockBody<T extends BlockType>(
|
|
|
691
692
|
|
|
692
693
|
const endSyncAggregate = stepsMetrics?.startTimer();
|
|
693
694
|
if (ForkSeq[fork] >= ForkSeq.altair) {
|
|
695
|
+
const parentBlockRoot = fromHex(parentBlock.blockRoot);
|
|
694
696
|
const previousSlot = slot - 1;
|
|
695
697
|
const syncAggregate = this.syncContributionAndProofPool.getAggregate(previousSlot, parentBlockRoot);
|
|
696
698
|
this.metrics?.production.producedSyncAggregateParticipants.observe(
|
|
@@ -18,6 +18,7 @@ export enum RegenCaller {
|
|
|
18
18
|
validateGossipAggregateAndProof = "validateGossipAggregateAndProof",
|
|
19
19
|
validateGossipAttestation = "validateGossipAttestation",
|
|
20
20
|
validateGossipVoluntaryExit = "validateGossipVoluntaryExit",
|
|
21
|
+
validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid",
|
|
21
22
|
onForkChoiceFinalized = "onForkChoiceFinalized",
|
|
22
23
|
restApi = "restApi",
|
|
23
24
|
}
|
|
@@ -26,7 +27,6 @@ export enum RegenFnName {
|
|
|
26
27
|
getBlockSlotState = "getBlockSlotState",
|
|
27
28
|
getState = "getState",
|
|
28
29
|
getPreState = "getPreState",
|
|
29
|
-
getCheckpointState = "getCheckpointState",
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export type StateRegenerationOpts = {
|
|
@@ -63,21 +63,11 @@ export interface IStateRegeneratorInternal {
|
|
|
63
63
|
rCaller: RegenCaller
|
|
64
64
|
): Promise<CachedBeaconStateAllForks>;
|
|
65
65
|
|
|
66
|
-
/**
|
|
67
|
-
* Return a valid checkpoint state
|
|
68
|
-
* This will always return a state with `state.slot % SLOTS_PER_EPOCH === 0`
|
|
69
|
-
*/
|
|
70
|
-
getCheckpointState(
|
|
71
|
-
cp: phase0.Checkpoint,
|
|
72
|
-
opts: StateRegenerationOpts,
|
|
73
|
-
rCaller: RegenCaller
|
|
74
|
-
): Promise<CachedBeaconStateAllForks>;
|
|
75
|
-
|
|
76
66
|
/**
|
|
77
67
|
* Return the state of `blockRoot` processed to slot `slot`
|
|
78
68
|
*/
|
|
79
69
|
getBlockSlotState(
|
|
80
|
-
|
|
70
|
+
block: ProtoBlock,
|
|
81
71
|
slot: Slot,
|
|
82
72
|
opts: StateRegenerationOpts,
|
|
83
73
|
rCaller: RegenCaller
|
|
@@ -5,7 +5,7 @@ import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
|
|
|
5
5
|
import {Logger, toRootHex} from "@lodestar/utils";
|
|
6
6
|
import {Metrics} from "../../metrics/index.js";
|
|
7
7
|
import {JobItemQueue} from "../../util/queue/index.js";
|
|
8
|
-
import {CheckpointHex
|
|
8
|
+
import {CheckpointHex} from "../stateCache/index.js";
|
|
9
9
|
import {BlockStateCache, CheckpointStateCache} from "../stateCache/types.js";
|
|
10
10
|
import {RegenError, RegenErrorCode} from "./errors.js";
|
|
11
11
|
import {
|
|
@@ -220,24 +220,6 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
220
220
|
return this.jobQueue.push({key: "getPreState", args: [block, opts, rCaller]});
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
async getCheckpointState(
|
|
224
|
-
cp: phase0.Checkpoint,
|
|
225
|
-
opts: StateRegenerationOpts,
|
|
226
|
-
rCaller: RegenCaller
|
|
227
|
-
): Promise<CachedBeaconStateAllForks> {
|
|
228
|
-
this.metrics?.regenFnCallTotal.inc({caller: rCaller, entrypoint: RegenFnName.getCheckpointState});
|
|
229
|
-
|
|
230
|
-
// First attempt to fetch the state from cache before queueing
|
|
231
|
-
const checkpointState = this.checkpointStateCache.get(toCheckpointHex(cp));
|
|
232
|
-
if (checkpointState) {
|
|
233
|
-
return checkpointState;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// The state is not immediately available in the caches, enqueue the job
|
|
237
|
-
this.metrics?.regenFnQueuedTotal.inc({caller: rCaller, entrypoint: RegenFnName.getCheckpointState});
|
|
238
|
-
return this.jobQueue.push({key: "getCheckpointState", args: [cp, opts, rCaller]});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
223
|
/**
|
|
242
224
|
* Get state of provided `blockRoot` and dial forward to `slot`
|
|
243
225
|
* Use this api with care because we don't want the queue to be busy
|
|
@@ -245,7 +227,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
245
227
|
* @returns
|
|
246
228
|
*/
|
|
247
229
|
async getBlockSlotState(
|
|
248
|
-
|
|
230
|
+
block: ProtoBlock,
|
|
249
231
|
slot: Slot,
|
|
250
232
|
opts: StateRegenerationOpts,
|
|
251
233
|
rCaller: RegenCaller
|
|
@@ -253,7 +235,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
253
235
|
this.metrics?.regenFnCallTotal.inc({caller: rCaller, entrypoint: RegenFnName.getBlockSlotState});
|
|
254
236
|
|
|
255
237
|
// The state is not immediately available in the caches, enqueue the job
|
|
256
|
-
return this.jobQueue.push({key: "getBlockSlotState", args: [
|
|
238
|
+
return this.jobQueue.push({key: "getBlockSlotState", args: [block, slot, opts, rCaller]});
|
|
257
239
|
}
|
|
258
240
|
|
|
259
241
|
async getState(stateRoot: RootHex, rCaller: RegenCaller): Promise<CachedBeaconStateAllForks> {
|
|
@@ -281,8 +263,6 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
281
263
|
switch (regenRequest.key) {
|
|
282
264
|
case "getPreState":
|
|
283
265
|
return await this.regen.getPreState(...regenRequest.args);
|
|
284
|
-
case "getCheckpointState":
|
|
285
|
-
return await this.regen.getCheckpointState(...regenRequest.args);
|
|
286
266
|
case "getBlockSlotState":
|
|
287
267
|
return await this.regen.getBlockSlotState(...regenRequest.args);
|
|
288
268
|
case "getState":
|
package/src/chain/regen/regen.ts
CHANGED
|
@@ -11,13 +11,14 @@ import {
|
|
|
11
11
|
processSlots,
|
|
12
12
|
stateTransition,
|
|
13
13
|
} from "@lodestar/state-transition";
|
|
14
|
-
import {BeaconBlock, RootHex, SignedBeaconBlock, Slot
|
|
14
|
+
import {BeaconBlock, RootHex, SignedBeaconBlock, Slot} from "@lodestar/types";
|
|
15
15
|
import {Logger, fromHex, toRootHex} from "@lodestar/utils";
|
|
16
16
|
import {IBeaconDb} from "../../db/index.js";
|
|
17
17
|
import {Metrics} from "../../metrics/index.js";
|
|
18
18
|
import {nextEventLoop} from "../../util/eventLoop.js";
|
|
19
19
|
import {getCheckpointFromState} from "../blocks/utils/checkpoint.js";
|
|
20
20
|
import {ChainEvent, ChainEventEmitter} from "../emitter.js";
|
|
21
|
+
import {SeenBlockInput} from "../seenCache/seenGossipBlockInput.js";
|
|
21
22
|
import {BlockStateCache, CheckpointStateCache} from "../stateCache/types.js";
|
|
22
23
|
import {ValidatorMonitor} from "../validatorMonitor.js";
|
|
23
24
|
import {RegenError, RegenErrorCode} from "./errors.js";
|
|
@@ -28,6 +29,7 @@ export type RegenModules = {
|
|
|
28
29
|
forkChoice: IForkChoice;
|
|
29
30
|
blockStateCache: BlockStateCache;
|
|
30
31
|
checkpointStateCache: CheckpointStateCache;
|
|
32
|
+
seenBlockInputCache: SeenBlockInput;
|
|
31
33
|
config: ChainForkConfig;
|
|
32
34
|
emitter: ChainEventEmitter;
|
|
33
35
|
logger: Logger;
|
|
@@ -74,45 +76,24 @@ export class StateRegenerator implements IStateRegeneratorInternal {
|
|
|
74
76
|
// We may have the checkpoint state with parent root inside the checkpoint state cache
|
|
75
77
|
// through gossip validation.
|
|
76
78
|
if (parentEpoch < blockEpoch) {
|
|
77
|
-
return this.
|
|
79
|
+
return this.getBlockSlotState(parentBlock, block.slot, opts, regenCaller, allowDiskReload);
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
// Otherwise, get the state normally.
|
|
81
83
|
return this.getState(parentBlock.stateRoot, regenCaller, allowDiskReload);
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
/**
|
|
85
|
-
* Get state after block `cp.root` dialed forward to first slot of `cp.epoch`
|
|
86
|
-
*/
|
|
87
|
-
async getCheckpointState(
|
|
88
|
-
cp: phase0.Checkpoint,
|
|
89
|
-
opts: StateRegenerationOpts,
|
|
90
|
-
regenCaller: RegenCaller,
|
|
91
|
-
allowDiskReload = false
|
|
92
|
-
): Promise<CachedBeaconStateAllForks> {
|
|
93
|
-
const checkpointStartSlot = computeStartSlotAtEpoch(cp.epoch);
|
|
94
|
-
return this.getBlockSlotState(toRootHex(cp.root), checkpointStartSlot, opts, regenCaller, allowDiskReload);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
86
|
/**
|
|
98
87
|
* Get state after block `blockRoot` dialed forward to `slot`
|
|
99
88
|
* - allowDiskReload should be used with care, as it will cause the state to be reloaded from disk
|
|
100
89
|
*/
|
|
101
90
|
async getBlockSlotState(
|
|
102
|
-
|
|
91
|
+
block: ProtoBlock,
|
|
103
92
|
slot: Slot,
|
|
104
93
|
opts: StateRegenerationOpts,
|
|
105
94
|
regenCaller: RegenCaller,
|
|
106
95
|
allowDiskReload = false
|
|
107
96
|
): Promise<CachedBeaconStateAllForks> {
|
|
108
|
-
const block = this.modules.forkChoice.getBlockHex(blockRoot);
|
|
109
|
-
if (!block) {
|
|
110
|
-
throw new RegenError({
|
|
111
|
-
code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,
|
|
112
|
-
blockRoot,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
97
|
if (slot < block.slot) {
|
|
117
98
|
throw new RegenError({
|
|
118
99
|
code: RegenErrorCode.SLOT_BEFORE_BLOCK_SLOT,
|
|
@@ -121,6 +102,7 @@ export class StateRegenerator implements IStateRegeneratorInternal {
|
|
|
121
102
|
});
|
|
122
103
|
}
|
|
123
104
|
|
|
105
|
+
const {blockRoot} = block;
|
|
124
106
|
const {checkpointStateCache} = this.modules;
|
|
125
107
|
const epoch = computeEpochAtSlot(slot);
|
|
126
108
|
const latestCheckpointStateCtx = allowDiskReload
|
|
@@ -211,7 +193,10 @@ export class StateRegenerator implements IStateRegeneratorInternal {
|
|
|
211
193
|
const protoBlocksAsc = blocksToReplay.reverse();
|
|
212
194
|
for (const [i, protoBlock] of protoBlocksAsc.entries()) {
|
|
213
195
|
replaySlots[i] = protoBlock.slot;
|
|
214
|
-
|
|
196
|
+
const blockInput = this.modules.seenBlockInputCache.get(protoBlock.blockRoot);
|
|
197
|
+
blockPromises[i] = blockInput?.hasBlock()
|
|
198
|
+
? Promise.resolve(blockInput.getBlock())
|
|
199
|
+
: this.modules.db.block.get(fromHex(protoBlock.blockRoot));
|
|
215
200
|
}
|
|
216
201
|
|
|
217
202
|
const logCtx = {stateRoot, caller, replaySlots: replaySlots.join(",")};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export {SeenAggregators, SeenAttesters} from "./seenAttesters.js";
|
|
1
|
+
export {SeenAggregators, SeenAttesters, SeenPayloadAttesters} from "./seenAttesters.js";
|
|
2
2
|
export {SeenBlockProposers} from "./seenBlockProposers.js";
|
|
3
3
|
export {SeenSyncCommitteeMessages} from "./seenCommittee.js";
|
|
4
4
|
export {SeenContributionAndProof} from "./seenCommitteeContribution.js";
|
|
5
|
+
export {SeenExecutionPayloadBids} from "./seenExecutionPayloadBids.js";
|
|
6
|
+
export {SeenExecutionPayloadEnvelopes} from "./seenExecutionPayloadEnvelope.js";
|
|
5
7
|
export {SeenBlockInput} from "./seenGossipBlockInput.js";
|
|
@@ -56,3 +56,8 @@ export class SeenAttesters {
|
|
|
56
56
|
* Keeps a cache to filter aggregated attestations from the same aggregators in the same epoch
|
|
57
57
|
*/
|
|
58
58
|
export class SeenAggregators extends SeenAttesters {}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Keeps a cache to filter payload attestations from the same attesters in the same epoch
|
|
62
|
+
*/
|
|
63
|
+
export class SeenPayloadAttesters extends SeenAttesters {}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {BuilderIndex, Slot} from "@lodestar/types";
|
|
2
|
+
import {MapDef} from "@lodestar/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TODO GLOAS: Revisit this value and add rational for choosing it
|
|
6
|
+
*/
|
|
7
|
+
const SLOTS_RETAINED = 2;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Tracks execution payload bids we've already seen per (slot, builder).
|
|
11
|
+
*/
|
|
12
|
+
export class SeenExecutionPayloadBids {
|
|
13
|
+
private readonly builderIndexesBySlot = new MapDef<Slot, Set<BuilderIndex>>(() => new Set<BuilderIndex>());
|
|
14
|
+
private lowestPermissibleSlot: Slot = 0;
|
|
15
|
+
|
|
16
|
+
isKnown(slot: Slot, builderIndex: BuilderIndex): boolean {
|
|
17
|
+
return this.builderIndexesBySlot.get(slot)?.has(builderIndex) === true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
add(slot: Slot, builderIndex: BuilderIndex): void {
|
|
21
|
+
if (slot < this.lowestPermissibleSlot) {
|
|
22
|
+
throw Error(`slot ${slot} < lowestPermissibleSlot ${this.lowestPermissibleSlot}`);
|
|
23
|
+
}
|
|
24
|
+
this.builderIndexesBySlot.getOrDefault(slot).add(builderIndex);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
prune(currentSlot: Slot): void {
|
|
28
|
+
this.lowestPermissibleSlot = Math.max(currentSlot - SLOTS_RETAINED, 0);
|
|
29
|
+
for (const slot of this.builderIndexesBySlot.keys()) {
|
|
30
|
+
if (slot < this.lowestPermissibleSlot) {
|
|
31
|
+
this.builderIndexesBySlot.delete(slot);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {RootHex, Slot} from "@lodestar/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cache to prevent processing multiple execution payload envelopes for the same block root.
|
|
5
|
+
* Only one builder qualifies to submit an execution payload for a given slot.
|
|
6
|
+
* We only keep track of envelopes of unfinalized slots.
|
|
7
|
+
* [IGNORE] The node has not seen another valid `SignedExecutionPayloadEnvelope` for this block root.
|
|
8
|
+
*/
|
|
9
|
+
export class SeenExecutionPayloadEnvelopes {
|
|
10
|
+
private readonly slotByBlockRoot = new Map<RootHex, Slot>();
|
|
11
|
+
private finalizedSlot: Slot = 0;
|
|
12
|
+
|
|
13
|
+
isKnown(blockRoot: RootHex): boolean {
|
|
14
|
+
return this.slotByBlockRoot.has(blockRoot);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
add(blockRoot: RootHex, slot: Slot): void {
|
|
18
|
+
if (slot < this.finalizedSlot) {
|
|
19
|
+
throw Error(`slot ${slot} < finalizedSlot ${this.finalizedSlot}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.slotByBlockRoot.set(blockRoot, slot);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
prune(finalizedSlot: Slot): void {
|
|
26
|
+
this.finalizedSlot = finalizedSlot;
|
|
27
|
+
|
|
28
|
+
for (const [blockRoot, slot] of this.slotByBlockRoot.entries()) {
|
|
29
|
+
if (slot < finalizedSlot) {
|
|
30
|
+
this.slotByBlockRoot.delete(blockRoot);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -379,7 +379,7 @@ export class SeenBlockInput {
|
|
|
379
379
|
let itemsToDelete = this.blockInputs.size - MAX_BLOCK_INPUT_CACHE_SIZE;
|
|
380
380
|
|
|
381
381
|
if (itemsToDelete > 0) {
|
|
382
|
-
const sorted = [...this.blockInputs.entries()].sort((a, b) =>
|
|
382
|
+
const sorted = [...this.blockInputs.entries()].sort((a, b) => a[1].slot - b[1].slot);
|
|
383
383
|
for (const [rootHex] of sorted) {
|
|
384
384
|
this.blockInputs.delete(rootHex);
|
|
385
385
|
itemsToDelete--;
|
|
@@ -71,11 +71,34 @@ async function validateAggregateAndProof(
|
|
|
71
71
|
const attData = aggregate.data;
|
|
72
72
|
const attSlot = attData.slot;
|
|
73
73
|
|
|
74
|
-
let
|
|
75
|
-
if (ForkSeq[fork] >= ForkSeq.
|
|
76
|
-
|
|
74
|
+
let committeeIndex: number | null;
|
|
75
|
+
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
76
|
+
// [REJECT] `aggregate.data.index < 2`.
|
|
77
|
+
if (attData.index >= 2) {
|
|
78
|
+
throw new AttestationError(GossipAction.REJECT, {
|
|
79
|
+
code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE,
|
|
80
|
+
attDataIndex: attData.index,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// [REJECT] `aggregate.data.index == 0` if `block.slot == aggregate.data.slot`.
|
|
84
|
+
const block = chain.forkChoice.getBlock(attData.beaconBlockRoot);
|
|
85
|
+
|
|
86
|
+
// If block is unknown, we don't handle it here. It will throw error later on at `verifyHeadBlockAndTargetRoot()`
|
|
87
|
+
if (block !== null && block.slot === attData.slot && attData.index !== 0) {
|
|
88
|
+
throw new AttestationError(GossipAction.REJECT, {
|
|
89
|
+
code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// [REJECT] len(committee_indices) == 1, where committee_indices = get_committee_indices(aggregate)
|
|
94
|
+
committeeIndex = (aggregate as electra.Attestation).committeeBits.getSingleTrueBit();
|
|
95
|
+
if (committeeIndex === null) {
|
|
96
|
+
throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.NOT_EXACTLY_ONE_COMMITTEE_BIT_SET});
|
|
97
|
+
}
|
|
98
|
+
} else if (ForkSeq[fork] >= ForkSeq.electra) {
|
|
99
|
+
committeeIndex = (aggregate as electra.Attestation).committeeBits.getSingleTrueBit();
|
|
77
100
|
// [REJECT] len(committee_indices) == 1, where committee_indices = get_committee_indices(aggregate)
|
|
78
|
-
if (
|
|
101
|
+
if (committeeIndex === null) {
|
|
79
102
|
throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.NOT_EXACTLY_ONE_COMMITTEE_BIT_SET});
|
|
80
103
|
}
|
|
81
104
|
// [REJECT] aggregate.data.index == 0
|
|
@@ -83,11 +106,11 @@ async function validateAggregateAndProof(
|
|
|
83
106
|
throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.NON_ZERO_ATTESTATION_DATA_INDEX});
|
|
84
107
|
}
|
|
85
108
|
} else {
|
|
86
|
-
|
|
109
|
+
committeeIndex = attData.index;
|
|
87
110
|
}
|
|
88
111
|
|
|
89
112
|
const seenAttDataKey = serializedData ? getSeenAttDataKeyFromSignedAggregateAndProof(fork, serializedData) : null;
|
|
90
|
-
const cachedAttData = seenAttDataKey ? chain.seenAttestationDatas.get(attSlot,
|
|
113
|
+
const cachedAttData = seenAttDataKey ? chain.seenAttestationDatas.get(attSlot, committeeIndex, seenAttDataKey) : null;
|
|
91
114
|
|
|
92
115
|
const attEpoch = computeEpochAtSlot(attSlot);
|
|
93
116
|
const attTarget = attData.target;
|
|
@@ -136,7 +159,7 @@ async function validateAggregateAndProof(
|
|
|
136
159
|
: toRootHex(ssz.phase0.AttestationData.hashTreeRoot(attData));
|
|
137
160
|
if (
|
|
138
161
|
!skipValidationKnownAttesters &&
|
|
139
|
-
chain.seenAggregatedAttestations.isKnown(targetEpoch,
|
|
162
|
+
chain.seenAggregatedAttestations.isKnown(targetEpoch, committeeIndex, attDataRootHex, aggregationBits)
|
|
140
163
|
) {
|
|
141
164
|
throw new AttestationError(GossipAction.IGNORE, {
|
|
142
165
|
code: AttestationErrorCode.ATTESTERS_ALREADY_KNOWN,
|
|
@@ -177,7 +200,7 @@ async function validateAggregateAndProof(
|
|
|
177
200
|
// -- i.e. data.index < get_committee_count_per_slot(state, data.target.epoch)
|
|
178
201
|
const committeeValidatorIndices = cachedAttData
|
|
179
202
|
? cachedAttData.committeeValidatorIndices
|
|
180
|
-
: getCommitteeValidatorIndices(shuffling, attSlot,
|
|
203
|
+
: getCommitteeValidatorIndices(shuffling, attSlot, committeeIndex);
|
|
181
204
|
|
|
182
205
|
// [REJECT] The number of aggregation bits matches the committee size
|
|
183
206
|
// -- i.e. `len(aggregation_bits) == len(get_beacon_committee(state, aggregate.data.slot, index))`.
|
|
@@ -216,16 +239,15 @@ async function validateAggregateAndProof(
|
|
|
216
239
|
// by the validator with index aggregate_and_proof.aggregator_index.
|
|
217
240
|
// [REJECT] The aggregator signature, signed_aggregate_and_proof.signature, is valid.
|
|
218
241
|
// [REJECT] The signature of aggregate is valid.
|
|
219
|
-
const aggregator = chain.index2pubkey[aggregateAndProof.aggregatorIndex];
|
|
220
242
|
const signingRoot = cachedAttData ? cachedAttData.signingRoot : getAttestationDataSigningRoot(chain.config, attData);
|
|
221
243
|
const indexedAttestationSignatureSet = createAggregateSignatureSetFromComponents(
|
|
222
|
-
indexedAttestation.attestingIndices
|
|
244
|
+
indexedAttestation.attestingIndices,
|
|
223
245
|
signingRoot,
|
|
224
246
|
indexedAttestation.signature
|
|
225
247
|
);
|
|
226
248
|
const signatureSets = [
|
|
227
|
-
getSelectionProofSignatureSet(chain.config, attSlot,
|
|
228
|
-
getAggregateAndProofSignatureSet(chain.config, attEpoch,
|
|
249
|
+
getSelectionProofSignatureSet(chain.config, attSlot, aggregatorIndex, signedAggregateAndProof),
|
|
250
|
+
getAggregateAndProofSignatureSet(chain.config, attEpoch, aggregatorIndex, signedAggregateAndProof),
|
|
229
251
|
indexedAttestationSignatureSet,
|
|
230
252
|
];
|
|
231
253
|
// no need to write to SeenAttestationDatas
|
|
@@ -248,7 +270,7 @@ async function validateAggregateAndProof(
|
|
|
248
270
|
// Same race-condition check as above for seen aggregators
|
|
249
271
|
if (
|
|
250
272
|
!skipValidationKnownAttesters &&
|
|
251
|
-
chain.seenAggregatedAttestations.isKnown(targetEpoch,
|
|
273
|
+
chain.seenAggregatedAttestations.isKnown(targetEpoch, committeeIndex, attDataRootHex, aggregationBits)
|
|
252
274
|
) {
|
|
253
275
|
throw new AttestationError(GossipAction.IGNORE, {
|
|
254
276
|
code: AttestationErrorCode.ATTESTERS_ALREADY_KNOWN,
|
|
@@ -260,7 +282,7 @@ async function validateAggregateAndProof(
|
|
|
260
282
|
chain.seenAggregators.add(targetEpoch, aggregatorIndex);
|
|
261
283
|
chain.seenAggregatedAttestations.add(
|
|
262
284
|
targetEpoch,
|
|
263
|
-
|
|
285
|
+
committeeIndex,
|
|
264
286
|
attDataRootHex,
|
|
265
287
|
{aggregationBits, trueBitCount: attestingIndices.length},
|
|
266
288
|
false
|
|
@@ -10,16 +10,17 @@ import {
|
|
|
10
10
|
ForkSeq,
|
|
11
11
|
SLOTS_PER_EPOCH,
|
|
12
12
|
isForkPostElectra,
|
|
13
|
+
isForkPostGloas,
|
|
13
14
|
} from "@lodestar/params";
|
|
14
15
|
import {
|
|
15
16
|
EpochShuffling,
|
|
17
|
+
IndexedSignatureSet,
|
|
16
18
|
ShufflingError,
|
|
17
19
|
ShufflingErrorCode,
|
|
18
|
-
SingleSignatureSet,
|
|
19
20
|
computeEpochAtSlot,
|
|
20
21
|
computeSigningRoot,
|
|
21
22
|
computeStartSlotAtEpoch,
|
|
22
|
-
|
|
23
|
+
createIndexedSignatureSetFromComponents,
|
|
23
24
|
} from "@lodestar/state-transition";
|
|
24
25
|
import {
|
|
25
26
|
CommitteeIndex,
|
|
@@ -89,7 +90,7 @@ export type GossipAttestation = {
|
|
|
89
90
|
};
|
|
90
91
|
|
|
91
92
|
export type Step0Result = AttestationValidationResult & {
|
|
92
|
-
signatureSet:
|
|
93
|
+
signatureSet: IndexedSignatureSet;
|
|
93
94
|
validatorIndex: number;
|
|
94
95
|
};
|
|
95
96
|
|
|
@@ -124,7 +125,7 @@ export async function validateGossipAttestationsSameAttData(
|
|
|
124
125
|
// step1: verify signatures of all valid attestations
|
|
125
126
|
// map new index to index in resultOrErrors
|
|
126
127
|
const newIndexToOldIndex = new Map<number, number>();
|
|
127
|
-
const signatureSets:
|
|
128
|
+
const signatureSets: IndexedSignatureSet[] = [];
|
|
128
129
|
let newIndex = 0;
|
|
129
130
|
const step0Results: Step0Result[] = [];
|
|
130
131
|
for (const [i, resultOrError] of step0ResultOrErrors.entries()) {
|
|
@@ -142,7 +143,7 @@ export async function validateGossipAttestationsSameAttData(
|
|
|
142
143
|
if (batchableBls) {
|
|
143
144
|
// all signature sets should have same signing root since we filtered in network processor
|
|
144
145
|
signatureValids = await chain.bls.verifySignatureSetsSameMessage(
|
|
145
|
-
signatureSets.map((set) => ({publicKey: set.
|
|
146
|
+
signatureSets.map((set) => ({publicKey: chain.index2pubkey[set.index], signature: set.signature})),
|
|
146
147
|
signatureSets[0].signingRoot
|
|
147
148
|
);
|
|
148
149
|
} else {
|
|
@@ -293,9 +294,29 @@ async function validateAttestationNoSignatureCheck(
|
|
|
293
294
|
// api or first time validation of a gossip attestation
|
|
294
295
|
committeeIndex = attestationOrCache.attestation.committeeIndex;
|
|
295
296
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
if (isForkPostGloas(fork)) {
|
|
298
|
+
// [REJECT] `attestation.data.index < 2`.
|
|
299
|
+
if (attData.index >= 2) {
|
|
300
|
+
throw new AttestationError(GossipAction.REJECT, {
|
|
301
|
+
code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE,
|
|
302
|
+
attDataIndex: attData.index,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// [REJECT] `attestation.data.index == 0` if `block.slot == attestation.data.slot`.
|
|
307
|
+
const block = chain.forkChoice.getBlock(attData.beaconBlockRoot);
|
|
308
|
+
|
|
309
|
+
// block being null will be handled by `verifyHeadBlockAndTargetRoot`
|
|
310
|
+
if (block !== null && block.slot === attSlot && attData.index !== 0) {
|
|
311
|
+
throw new AttestationError(GossipAction.REJECT, {
|
|
312
|
+
code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
// [REJECT] attestation.data.index == 0
|
|
317
|
+
if (attData.index !== 0) {
|
|
318
|
+
throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.NON_ZERO_ATTESTATION_DATA_INDEX});
|
|
319
|
+
}
|
|
299
320
|
}
|
|
300
321
|
} else {
|
|
301
322
|
// phase0 attestation
|
|
@@ -477,7 +498,7 @@ async function validateAttestationNoSignatureCheck(
|
|
|
477
498
|
|
|
478
499
|
// [REJECT] The signature of attestation is valid.
|
|
479
500
|
const attestingIndices = [validatorIndex];
|
|
480
|
-
let signatureSet:
|
|
501
|
+
let signatureSet: IndexedSignatureSet;
|
|
481
502
|
let attDataRootHex: RootHex;
|
|
482
503
|
const signature = attestationOrCache.attestation
|
|
483
504
|
? attestationOrCache.attestation.signature
|
|
@@ -492,18 +513,14 @@ async function validateAttestationNoSignatureCheck(
|
|
|
492
513
|
|
|
493
514
|
if (attestationOrCache.cache) {
|
|
494
515
|
// there could be up to 6% of cpu time to compute signing root if we don't clone the signature set
|
|
495
|
-
signatureSet =
|
|
496
|
-
|
|
516
|
+
signatureSet = createIndexedSignatureSetFromComponents(
|
|
517
|
+
validatorIndex,
|
|
497
518
|
attestationOrCache.cache.signingRoot,
|
|
498
519
|
signature
|
|
499
520
|
);
|
|
500
521
|
attDataRootHex = attestationOrCache.cache.attDataRootHex;
|
|
501
522
|
} else {
|
|
502
|
-
signatureSet =
|
|
503
|
-
chain.index2pubkey[validatorIndex],
|
|
504
|
-
getSigningRoot(),
|
|
505
|
-
signature
|
|
506
|
-
);
|
|
523
|
+
signatureSet = createIndexedSignatureSetFromComponents(validatorIndex, getSigningRoot(), signature);
|
|
507
524
|
|
|
508
525
|
// add cached attestation data before verifying signature
|
|
509
526
|
attDataRootHex = toRootHex(ssz.phase0.AttestationData.hashTreeRoot(attData));
|
|
@@ -58,12 +58,7 @@ export async function validateAttesterSlashing(
|
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
const signatureSets = getAttesterSlashingSignatureSets(
|
|
62
|
-
chain.config,
|
|
63
|
-
chain.index2pubkey,
|
|
64
|
-
state.slot,
|
|
65
|
-
attesterSlashing
|
|
66
|
-
);
|
|
61
|
+
const signatureSets = getAttesterSlashingSignatureSets(chain.config, state.slot, attesterSlashing);
|
|
67
62
|
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
|
|
68
63
|
throw new AttesterSlashingError(GossipAction.REJECT, {
|
|
69
64
|
code: AttesterSlashingErrorCode.INVALID,
|
|
@@ -124,7 +124,7 @@ export async function validateGossipBlobSidecar(
|
|
|
124
124
|
// [IGNORE] The block's parent (defined by block.parent_root) has been seen (via both gossip and non-gossip sources) (a client MAY queue blocks for processing once the parent block is retrieved).
|
|
125
125
|
// [REJECT] The block's parent (defined by block.parent_root) passes validation.
|
|
126
126
|
const blockState = await chain.regen
|
|
127
|
-
.getBlockSlotState(
|
|
127
|
+
.getBlockSlotState(parentBlock, blobSlot, {dontTransferCache: true}, RegenCaller.validateGossipBlock)
|
|
128
128
|
.catch(() => {
|
|
129
129
|
throw new BlobSidecarGossipError(GossipAction.IGNORE, {
|
|
130
130
|
code: BlobSidecarErrorCode.PARENT_UNKNOWN,
|
|
@@ -139,7 +139,6 @@ export async function validateGossipBlobSidecar(
|
|
|
139
139
|
if (!chain.seenBlockInputCache.isVerifiedProposerSignature(blobSlot, blockHex, signature)) {
|
|
140
140
|
const signatureSet = getBlockHeaderProposerSignatureSetByParentStateSlot(
|
|
141
141
|
chain.config,
|
|
142
|
-
chain.index2pubkey,
|
|
143
142
|
blockState.slot,
|
|
144
143
|
blobSidecar.signedBlockHeader
|
|
145
144
|
);
|
|
@@ -244,11 +243,7 @@ export async function validateBlockBlobSidecars(
|
|
|
244
243
|
const blockRootHex = toRootHex(blockRoot);
|
|
245
244
|
const signature = firstSidecarSignedBlockHeader.signature;
|
|
246
245
|
if (!chain.seenBlockInputCache.isVerifiedProposerSignature(blockSlot, blockRootHex, signature)) {
|
|
247
|
-
const signatureSet = getBlockHeaderProposerSignatureSetByHeaderSlot(
|
|
248
|
-
chain.config,
|
|
249
|
-
chain.index2pubkey,
|
|
250
|
-
firstSidecarSignedBlockHeader
|
|
251
|
-
);
|
|
246
|
+
const signatureSet = getBlockHeaderProposerSignatureSetByHeaderSlot(chain.config, firstSidecarSignedBlockHeader);
|
|
252
247
|
|
|
253
248
|
if (
|
|
254
249
|
!(await chain.bls.verifySignatureSets([signatureSet], {
|