@lodestar/beacon-node 1.40.0-dev.c7b6a784da → 1.40.0-dev.cfd894719f
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/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 +7 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +22 -5
- 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/regen/interface.d.ts +1 -0
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +1 -0
- package/lib/chain/regen/interface.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/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 +2 -2
- 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 +2 -2
- 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 +103 -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/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/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 +25 -3
- 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/regen/interface.ts +1 -0
- 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/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 +1 -6
- package/src/chain/validation/block.ts +5 -4
- package/src/chain/validation/dataColumnSidecar.ts +1 -6
- package/src/chain/validation/executionPayloadBid.ts +140 -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/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
|
@@ -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,
|
|
@@ -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], {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {ForkName, isForkPostDeneb} from "@lodestar/params";
|
|
2
|
+
import {ForkName, isForkPostBellatrix, isForkPostDeneb, isForkPostGloas} from "@lodestar/params";
|
|
3
3
|
import {
|
|
4
4
|
computeEpochAtSlot,
|
|
5
5
|
computeStartSlotAtEpoch,
|
|
@@ -111,7 +111,7 @@ export async function validateGossipBlock(
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in Consensus Layer -- i.e. validate that len(body.signed_beacon_block.message.blob_kzg_commitments) <= MAX_BLOBS_PER_BLOCK
|
|
114
|
-
if (isForkPostDeneb(fork)) {
|
|
114
|
+
if (isForkPostDeneb(fork) && !isForkPostGloas(fork)) {
|
|
115
115
|
const blobKzgCommitmentsLen = (block as deneb.BeaconBlock).body.blobKzgCommitments.length;
|
|
116
116
|
const maxBlobsPerBlock = config.getMaxBlobsPerBlock(computeEpochAtSlot(blockSlot));
|
|
117
117
|
if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
|
|
@@ -128,6 +128,7 @@ export async function validateGossipBlock(
|
|
|
128
128
|
// this is something we should change this in the future to make the code airtight to the spec.
|
|
129
129
|
// [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).
|
|
130
130
|
// [REJECT] The block's parent (defined by block.parent_root) passes validation.
|
|
131
|
+
// TODO GLOAS: post-gloas, we check the validity of bid's parent payload, not the entire beacon block
|
|
131
132
|
const blockState = await chain.regen
|
|
132
133
|
.getPreState(block, {dontTransferCache: true}, RegenCaller.validateGossipBlock)
|
|
133
134
|
.catch(() => {
|
|
@@ -140,7 +141,7 @@ export async function validateGossipBlock(
|
|
|
140
141
|
// Extra conditions for merge fork blocks
|
|
141
142
|
// [REJECT] The block's execution payload timestamp is correct with respect to the slot
|
|
142
143
|
// -- i.e. execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot).
|
|
143
|
-
if (fork
|
|
144
|
+
if (isForkPostBellatrix(fork) && !isForkPostGloas(fork)) {
|
|
144
145
|
if (!isExecutionBlockBodyType(block.body)) throw Error("Not merge block type");
|
|
145
146
|
const executionPayload = block.body.executionPayload;
|
|
146
147
|
if (isExecutionStateType(blockState) && isExecutionEnabled(blockState, block)) {
|
|
@@ -157,7 +158,7 @@ export async function validateGossipBlock(
|
|
|
157
158
|
|
|
158
159
|
// [REJECT] The proposer signature, signed_beacon_block.signature, is valid with respect to the proposer_index pubkey.
|
|
159
160
|
if (!chain.seenBlockInputCache.isVerifiedProposerSignature(blockSlot, blockRoot, signedBlock.signature)) {
|
|
160
|
-
const signatureSet = getBlockProposerSignatureSet(chain.config,
|
|
161
|
+
const signatureSet = getBlockProposerSignatureSet(chain.config, signedBlock);
|
|
161
162
|
// Don't batch so verification is not delayed
|
|
162
163
|
if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: true}))) {
|
|
163
164
|
throw new BlockGossipError(GossipAction.REJECT, {
|
|
@@ -136,7 +136,6 @@ export async function validateGossipDataColumnSidecar(
|
|
|
136
136
|
if (!chain.seenBlockInputCache.isVerifiedProposerSignature(blockHeader.slot, blockRootHex, signature)) {
|
|
137
137
|
const signatureSet = getBlockHeaderProposerSignatureSetByParentStateSlot(
|
|
138
138
|
chain.config,
|
|
139
|
-
chain.index2pubkey,
|
|
140
139
|
blockState.slot,
|
|
141
140
|
dataColumnSidecar.signedBlockHeader
|
|
142
141
|
);
|
|
@@ -337,11 +336,7 @@ export async function validateBlockDataColumnSidecars(
|
|
|
337
336
|
const slot = firstSidecarSignedBlockHeader.message.slot;
|
|
338
337
|
const signature = firstSidecarSignedBlockHeader.signature;
|
|
339
338
|
if (!chain.seenBlockInputCache.isVerifiedProposerSignature(slot, rootHex, signature)) {
|
|
340
|
-
const signatureSet = getBlockHeaderProposerSignatureSetByHeaderSlot(
|
|
341
|
-
chain.config,
|
|
342
|
-
chain.index2pubkey,
|
|
343
|
-
firstSidecarSignedBlockHeader
|
|
344
|
-
);
|
|
339
|
+
const signatureSet = getBlockHeaderProposerSignatureSetByHeaderSlot(chain.config, firstSidecarSignedBlockHeader);
|
|
345
340
|
|
|
346
341
|
if (
|
|
347
342
|
!(await chain.bls.verifySignatureSets([signatureSet], {
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import {PublicKey} from "@chainsafe/blst";
|
|
2
|
+
import {
|
|
3
|
+
CachedBeaconStateGloas,
|
|
4
|
+
canBuilderCoverBid,
|
|
5
|
+
createSingleSignatureSetFromComponents,
|
|
6
|
+
getExecutionPayloadBidSigningRoot,
|
|
7
|
+
isActiveBuilder,
|
|
8
|
+
} from "@lodestar/state-transition";
|
|
9
|
+
import {gloas} from "@lodestar/types";
|
|
10
|
+
import {toRootHex} from "@lodestar/utils";
|
|
11
|
+
import {ExecutionPayloadBidError, ExecutionPayloadBidErrorCode, GossipAction} from "../errors/index.js";
|
|
12
|
+
import {IBeaconChain} from "../index.js";
|
|
13
|
+
import {RegenCaller} from "../regen/index.js";
|
|
14
|
+
|
|
15
|
+
export async function validateApiExecutionPayloadBid(
|
|
16
|
+
chain: IBeaconChain,
|
|
17
|
+
signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
|
|
18
|
+
): Promise<void> {
|
|
19
|
+
return validateExecutionPayloadBid(chain, signedExecutionPayloadBid);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function validateGossipExecutionPayloadBid(
|
|
23
|
+
chain: IBeaconChain,
|
|
24
|
+
signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
return validateExecutionPayloadBid(chain, signedExecutionPayloadBid);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function validateExecutionPayloadBid(
|
|
30
|
+
chain: IBeaconChain,
|
|
31
|
+
signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
|
|
32
|
+
): Promise<void> {
|
|
33
|
+
const bid = signedExecutionPayloadBid.message;
|
|
34
|
+
const parentBlockRootHex = toRootHex(bid.parentBlockRoot);
|
|
35
|
+
const parentBlockHashHex = toRootHex(bid.parentBlockHash);
|
|
36
|
+
const state = (await chain.getHeadStateAtCurrentEpoch(
|
|
37
|
+
RegenCaller.validateGossipExecutionPayloadBid
|
|
38
|
+
)) as CachedBeaconStateGloas;
|
|
39
|
+
|
|
40
|
+
// [IGNORE] `bid.slot` is the current slot or the next slot.
|
|
41
|
+
const currentSlot = chain.clock.currentSlot;
|
|
42
|
+
if (bid.slot !== currentSlot && bid.slot !== currentSlot + 1) {
|
|
43
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
44
|
+
code: ExecutionPayloadBidErrorCode.INVALID_SLOT,
|
|
45
|
+
builderIndex: bid.builderIndex,
|
|
46
|
+
slot: bid.slot,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// [IGNORE] the `SignedProposerPreferences` where `preferences.proposal_slot`
|
|
51
|
+
// is equal to `bid.slot` has been seen.
|
|
52
|
+
// TODO GLOAS: Implement this along with proposer preference
|
|
53
|
+
|
|
54
|
+
// [REJECT] `bid.builder_index` is a valid/active builder index -- i.e.
|
|
55
|
+
// `is_active_builder(state, bid.builder_index)` returns `True`.
|
|
56
|
+
if (!isActiveBuilder(state, bid.builderIndex)) {
|
|
57
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
58
|
+
code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
|
|
59
|
+
builderIndex: bid.builderIndex,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// [REJECT] `bid.execution_payment` is zero.
|
|
64
|
+
if (bid.executionPayment !== 0) {
|
|
65
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
66
|
+
code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT,
|
|
67
|
+
builderIndex: bid.builderIndex,
|
|
68
|
+
executionPayment: bid.executionPayment,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// [REJECT] `bid.fee_recipient` matches the `fee_recipient` from the proposer's
|
|
73
|
+
// `SignedProposerPreferences` associated with `bid.slot`.
|
|
74
|
+
// [REJECT] `bid.gas_limit` matches the `gas_limit` from the proposer's
|
|
75
|
+
// `SignedProposerPreferences` associated with `bid.slot`.
|
|
76
|
+
// TODO GLOAS: Implement this along with proposer preference
|
|
77
|
+
|
|
78
|
+
// [IGNORE] this is the first signed bid seen with a valid signature from the given builder for this slot.
|
|
79
|
+
if (chain.seenExecutionPayloadBids.isKnown(bid.slot, bid.builderIndex)) {
|
|
80
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
81
|
+
code: ExecutionPayloadBidErrorCode.BID_ALREADY_KNOWN,
|
|
82
|
+
builderIndex: bid.builderIndex,
|
|
83
|
+
slot: bid.slot,
|
|
84
|
+
parentBlockRoot: parentBlockRootHex,
|
|
85
|
+
parentBlockHash: parentBlockHashHex,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// [IGNORE] this bid is the highest value bid seen for the corresponding slot
|
|
90
|
+
// and the given parent block hash.
|
|
91
|
+
const bestBid = chain.executionPayloadBidPool.getBestBid(parentBlockRootHex, parentBlockHashHex, bid.slot);
|
|
92
|
+
if (bestBid !== null && bestBid.value >= bid.value) {
|
|
93
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
94
|
+
code: ExecutionPayloadBidErrorCode.BID_TOO_LOW,
|
|
95
|
+
bidValue: bid.value,
|
|
96
|
+
currentHighestBid: bestBid.value,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// [IGNORE] `bid.value` is less or equal than the builder's excess balance --
|
|
100
|
+
// i.e. `can_builder_cover_bid(state, builder_index, amount)` returns `True`.
|
|
101
|
+
if (!canBuilderCoverBid(state, bid.builderIndex, bid.value)) {
|
|
102
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
103
|
+
code: ExecutionPayloadBidErrorCode.BID_TOO_HIGH,
|
|
104
|
+
bidValue: bid.value,
|
|
105
|
+
builderBalance: state.builders.getReadonly(bid.builderIndex).balance,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// [IGNORE] `bid.parent_block_hash` is the block hash of a known execution
|
|
110
|
+
// payload in fork choice.
|
|
111
|
+
// TODO GLOAS: implement this
|
|
112
|
+
|
|
113
|
+
// [IGNORE] `bid.parent_block_root` is the hash tree root of a known beacon
|
|
114
|
+
// block in fork choice.
|
|
115
|
+
const block = chain.forkChoice.getBlock(bid.parentBlockRoot);
|
|
116
|
+
if (block === null) {
|
|
117
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
118
|
+
code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
119
|
+
parentBlockRoot: parentBlockRootHex,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// [REJECT] `signed_execution_payload_bid.signature` is valid with respect to the `bid.builder_index`.
|
|
124
|
+
const signatureSet = createSingleSignatureSetFromComponents(
|
|
125
|
+
PublicKey.fromBytes(state.builders.getReadonly(bid.builderIndex).pubkey),
|
|
126
|
+
getExecutionPayloadBidSigningRoot(chain.config, state as CachedBeaconStateGloas, bid),
|
|
127
|
+
signedExecutionPayloadBid.signature
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
if (!(await chain.bls.verifySignatureSets([signatureSet]))) {
|
|
131
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
132
|
+
code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE,
|
|
133
|
+
builderIndex: bid.builderIndex,
|
|
134
|
+
slot: bid.slot,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Valid
|
|
139
|
+
chain.seenExecutionPayloadBids.add(bid.slot, bid.builderIndex);
|
|
140
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {PublicKey} from "@chainsafe/blst";
|
|
2
|
+
import {
|
|
3
|
+
CachedBeaconStateGloas,
|
|
4
|
+
computeStartSlotAtEpoch,
|
|
5
|
+
createSingleSignatureSetFromComponents,
|
|
6
|
+
getExecutionPayloadEnvelopeSigningRoot,
|
|
7
|
+
} from "@lodestar/state-transition";
|
|
8
|
+
import {gloas} from "@lodestar/types";
|
|
9
|
+
import {toRootHex} from "@lodestar/utils";
|
|
10
|
+
import {ExecutionPayloadEnvelopeError, ExecutionPayloadEnvelopeErrorCode, GossipAction} from "../errors/index.js";
|
|
11
|
+
import {IBeaconChain} from "../index.js";
|
|
12
|
+
|
|
13
|
+
export async function validateApiExecutionPayloadEnvelope(
|
|
14
|
+
chain: IBeaconChain,
|
|
15
|
+
executionPayloadEnvelope: gloas.SignedExecutionPayloadEnvelope
|
|
16
|
+
): Promise<void> {
|
|
17
|
+
return validateExecutionPayloadEnvelope(chain, executionPayloadEnvelope);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function validateGossipExecutionPayloadEnvelope(
|
|
21
|
+
chain: IBeaconChain,
|
|
22
|
+
executionPayloadEnvelope: gloas.SignedExecutionPayloadEnvelope
|
|
23
|
+
): Promise<void> {
|
|
24
|
+
return validateExecutionPayloadEnvelope(chain, executionPayloadEnvelope);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function validateExecutionPayloadEnvelope(
|
|
28
|
+
chain: IBeaconChain,
|
|
29
|
+
executionPayloadEnvelope: gloas.SignedExecutionPayloadEnvelope
|
|
30
|
+
): Promise<void> {
|
|
31
|
+
const envelope = executionPayloadEnvelope.message;
|
|
32
|
+
const {payload} = envelope;
|
|
33
|
+
const blockRootHex = toRootHex(envelope.beaconBlockRoot);
|
|
34
|
+
|
|
35
|
+
// [IGNORE] The envelope's block root `envelope.block_root` has been seen (via
|
|
36
|
+
// gossip or non-gossip sources) (a client MAY queue payload for processing once
|
|
37
|
+
// the block is retrieved).
|
|
38
|
+
// TODO GLOAS: Need to review this
|
|
39
|
+
const block = chain.forkChoice.getBlock(envelope.beaconBlockRoot);
|
|
40
|
+
if (block === null) {
|
|
41
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
42
|
+
code: ExecutionPayloadEnvelopeErrorCode.BLOCK_ROOT_UNKNOWN,
|
|
43
|
+
blockRoot: blockRootHex,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// [IGNORE] The node has not seen another valid
|
|
48
|
+
// `SignedExecutionPayloadEnvelope` for this block root from this builder.
|
|
49
|
+
if (chain.seenExecutionPayloadEnvelopes.isKnown(blockRootHex)) {
|
|
50
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
51
|
+
code: ExecutionPayloadEnvelopeErrorCode.ENVELOPE_ALREADY_KNOWN,
|
|
52
|
+
blockRoot: blockRootHex,
|
|
53
|
+
slot: envelope.slot,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `envelope.slot >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
|
|
58
|
+
const finalizedCheckpoint = chain.forkChoice.getFinalizedCheckpoint();
|
|
59
|
+
const finalizedSlot = computeStartSlotAtEpoch(finalizedCheckpoint.epoch);
|
|
60
|
+
if (envelope.slot < finalizedSlot) {
|
|
61
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
62
|
+
code: ExecutionPayloadEnvelopeErrorCode.BELONG_TO_FINALIZED_BLOCK,
|
|
63
|
+
envelopeSlot: envelope.slot,
|
|
64
|
+
finalizedSlot,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// [REJECT] `block` passes validation.
|
|
69
|
+
// TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
|
|
70
|
+
// it is possible that the block didn't pass the validation
|
|
71
|
+
|
|
72
|
+
// [REJECT] `block.slot` equals `envelope.slot`.
|
|
73
|
+
if (block.slot !== envelope.slot) {
|
|
74
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
75
|
+
code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH,
|
|
76
|
+
envelopeSlot: envelope.slot,
|
|
77
|
+
blockSlot: block.slot,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (block.builderIndex === undefined || block.blockHashHex === undefined) {
|
|
82
|
+
// This indicates this block is a pre-gloas block which is wrong
|
|
83
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
84
|
+
code: ExecutionPayloadEnvelopeErrorCode.CACHE_FAIL,
|
|
85
|
+
blockRoot: blockRootHex,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// [REJECT] `envelope.builder_index == bid.builder_index`
|
|
90
|
+
if (envelope.builderIndex !== block.builderIndex) {
|
|
91
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
92
|
+
code: ExecutionPayloadEnvelopeErrorCode.BUILDER_INDEX_MISMATCH,
|
|
93
|
+
envelopeBuilderIndex: envelope.builderIndex,
|
|
94
|
+
bidBuilderIndex: block.builderIndex,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// [REJECT] `payload.block_hash == bid.block_hash`
|
|
99
|
+
if (toRootHex(payload.blockHash) !== block.blockHashHex) {
|
|
100
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
101
|
+
code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
|
|
102
|
+
envelopeBlockHash: toRootHex(payload.blockHash),
|
|
103
|
+
bidBlockHash: block.blockHashHex,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// [REJECT] `signed_execution_payload_envelope.signature` is valid with respect to the builder's public key.
|
|
108
|
+
const state = chain.getHeadState() as CachedBeaconStateGloas;
|
|
109
|
+
const signatureSet = createSingleSignatureSetFromComponents(
|
|
110
|
+
PublicKey.fromBytes(state.builders.getReadonly(envelope.builderIndex).pubkey),
|
|
111
|
+
getExecutionPayloadEnvelopeSigningRoot(chain.config, envelope),
|
|
112
|
+
executionPayloadEnvelope.signature
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (!(await chain.bls.verifySignatureSets([signatureSet]))) {
|
|
116
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
117
|
+
code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
chain.seenExecutionPayloadEnvelopes.add(blockRootHex, envelope.slot);
|
|
122
|
+
}
|