Skip to content

Commit 508459f

Browse files
Ability to add deposit receipts in the stub (Consensys#8139)
1 parent 15f5a84 commit 508459f

File tree

7 files changed

+114
-22
lines changed

7 files changed

+114
-22
lines changed

Diff for: ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImpl.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public ExecutionPayloadResult initiateBlockProduction(
7878
if (!isBlind) {
7979
final SafeFuture<GetPayloadResponse> getPayloadResponseFuture =
8080
executionLayerChannel
81-
.engineGetPayload(context, blockSlotState.getSlot())
81+
.engineGetPayload(context, blockSlotState)
8282
.thenPeek(__ -> blockProductionPerformance.engineGetPayload());
8383
final SafeFuture<ExecutionPayload> executionPayloadFuture =
8484
getPayloadResponseFuture.thenApply(GetPayloadResponse::getExecutionPayload);
@@ -111,7 +111,7 @@ public ExecutionPayloadResult initiateBlockAndBlobsProduction(
111111
if (!isBlind) {
112112
final SafeFuture<GetPayloadResponse> getPayloadResponseFuture =
113113
executionLayerChannel
114-
.engineGetPayload(context, blockSlotState.getSlot())
114+
.engineGetPayload(context, blockSlotState)
115115
.thenPeek(__ -> blockProductionPerformance.engineGetPayload());
116116
final SafeFuture<ExecutionPayload> executionPayloadFuture =
117117
getPayloadResponseFuture.thenApply(GetPayloadResponse::getExecutionPayload);

Diff for: ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerImpl.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,8 @@ public SafeFuture<ForkChoiceUpdatedResult> engineForkChoiceUpdated(
195195

196196
@Override
197197
public SafeFuture<GetPayloadResponse> engineGetPayload(
198-
final ExecutionPayloadContext executionPayloadContext, final UInt64 slot) {
199-
return engineGetPayload(executionPayloadContext, slot, false)
198+
final ExecutionPayloadContext executionPayloadContext, final BeaconState state) {
199+
return engineGetPayload(executionPayloadContext, state.getSlot(), false)
200200
.thenPeek(__ -> recordExecutionPayloadFallbackSource(Source.LOCAL_EL, FallbackReason.NONE));
201201
}
202202

Diff for: ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerStub.java

+5-7
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ public class ExecutionLayerManagerStub extends ExecutionLayerChannelStub
3737
private final BuilderCircuitBreaker builderCircuitBreaker;
3838

3939
public ExecutionLayerManagerStub(
40-
Spec spec,
41-
TimeProvider timeProvider,
42-
boolean enableTransitionEmulation,
40+
final Spec spec,
41+
final TimeProvider timeProvider,
42+
final boolean enableTransitionEmulation,
4343
final Optional<Bytes32> terminalBlockHashInTTDMode,
4444
final BuilderCircuitBreaker builderCircuitBreaker) {
4545
super(spec, timeProvider, enableTransitionEmulation, terminalBlockHashInTTDMode);
@@ -58,7 +58,7 @@ public SafeFuture<HeaderWithFallbackData> builderGetHeader(
5858
final SafeFuture<UInt256> payloadValueResult,
5959
final Optional<UInt64> requestedBuilderBoostFactor,
6060
final BlockProductionPerformance blockProductionPerformance) {
61-
boolean builderCircuitBreakerEngaged = builderCircuitBreaker.isEngaged(state);
61+
final boolean builderCircuitBreakerEngaged = builderCircuitBreaker.isEngaged(state);
6262
LOG.info("Builder Circuit Breaker isEngaged: " + builderCircuitBreakerEngaged);
6363

6464
return super.builderGetHeader(
@@ -70,9 +70,7 @@ public SafeFuture<HeaderWithFallbackData> builderGetHeader(
7070
.thenCompose(
7171
headerWithFallbackData -> {
7272
if (builderCircuitBreakerEngaged) {
73-
return engineGetPayload(
74-
executionPayloadContext,
75-
executionPayloadContext.getPayloadBuildingAttributes().getProposalSlot())
73+
return engineGetPayload(executionPayloadContext, state)
7674
.thenApply(
7775
payload ->
7876
HeaderWithFallbackData.create(

Diff for: ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerImplTest.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,12 @@ public void engineGetPayload_shouldReturnGetPayloadResponseViaEngine() {
166166
final ExecutionPayloadContext executionPayloadContext =
167167
dataStructureUtil.randomPayloadExecutionContext(false, true);
168168
final UInt64 slot = executionPayloadContext.getForkChoiceState().getHeadBlockSlot();
169+
final BeaconState state = dataStructureUtil.randomBeaconState(slot);
169170

170171
final GetPayloadResponse getPayloadResponse =
171172
prepareEngineGetPayloadResponse(executionPayloadContext, localExecutionPayloadValue, slot);
172173

173-
assertThat(executionLayerManager.engineGetPayload(executionPayloadContext, slot))
174+
assertThat(executionLayerManager.engineGetPayload(executionPayloadContext, state))
174175
.isCompletedWithValue(getPayloadResponse);
175176

176177
// we expect no calls to builder
@@ -187,11 +188,12 @@ public void engineGetPayloadV2_shouldReturnPayloadViaEngine() {
187188
dataStructureUtil.randomPayloadExecutionContext(false, true);
188189
executionLayerManager = createExecutionLayerChannelImpl(false, false);
189190
final UInt64 slot = executionPayloadContext.getForkChoiceState().getHeadBlockSlot();
191+
final BeaconState state = dataStructureUtil.randomBeaconState(slot);
190192

191193
final GetPayloadResponse getPayloadResponse =
192194
prepareEngineGetPayloadResponse(executionPayloadContext, localExecutionPayloadValue, slot);
193195

194-
assertThat(executionLayerManager.engineGetPayload(executionPayloadContext, slot))
196+
assertThat(executionLayerManager.engineGetPayload(executionPayloadContext, state))
195197
.isCompletedWithValue(getPayloadResponse);
196198

197199
// we expect no calls to builder
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright Consensys Software Inc., 2024
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package tech.pegasys.teku.spec.datastructures.util;
15+
16+
import java.security.SecureRandom;
17+
import java.util.List;
18+
import java.util.stream.IntStream;
19+
import org.apache.tuweni.bytes.Bytes32;
20+
import tech.pegasys.teku.bls.BLS;
21+
import tech.pegasys.teku.bls.BLSKeyPair;
22+
import tech.pegasys.teku.bls.BLSPublicKey;
23+
import tech.pegasys.teku.bls.BLSSignature;
24+
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
25+
import tech.pegasys.teku.spec.Spec;
26+
import tech.pegasys.teku.spec.constants.Domain;
27+
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt;
28+
import tech.pegasys.teku.spec.datastructures.operations.DepositMessage;
29+
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
30+
import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers;
31+
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra;
32+
33+
public class DepositReceiptsUtil {
34+
35+
private static final int MAX_NUMBER_OF_DEPOSITS_PER_BLOCK = 3;
36+
37+
private final Spec spec;
38+
39+
@SuppressWarnings("DoNotCreateSecureRandomDirectly")
40+
private final SecureRandom random = new SecureRandom();
41+
42+
public DepositReceiptsUtil(final Spec spec) {
43+
this.spec = spec;
44+
}
45+
46+
public List<DepositReceipt> generateDepositReceipts(final BeaconState state) {
47+
final UInt64 nextDepositReceiptIndex = UInt64.valueOf(state.getValidators().size());
48+
return IntStream.range(0, getNumberOfDepositReceiptsToGenerate())
49+
.mapToObj(i -> createDepositReceipt(state.getSlot(), nextDepositReceiptIndex.plus(i)))
50+
.toList();
51+
}
52+
53+
private int getNumberOfDepositReceiptsToGenerate() {
54+
return random.nextInt(MAX_NUMBER_OF_DEPOSITS_PER_BLOCK + 1);
55+
}
56+
57+
private DepositReceipt createDepositReceipt(final UInt64 slot, final UInt64 index) {
58+
final BLSKeyPair validatorKeyPair = BLSKeyPair.random(random);
59+
final BLSPublicKey publicKey = validatorKeyPair.getPublicKey();
60+
final UInt64 depositAmount = UInt64.THIRTY_TWO_ETH;
61+
final DepositMessage depositMessage =
62+
new DepositMessage(publicKey, Bytes32.ZERO, depositAmount);
63+
final MiscHelpers miscHelpers = spec.atSlot(slot).miscHelpers();
64+
final Bytes32 depositDomain = miscHelpers.computeDomain(Domain.DEPOSIT);
65+
final BLSSignature signature =
66+
BLS.sign(
67+
validatorKeyPair.getSecretKey(),
68+
miscHelpers.computeSigningRoot(depositMessage, depositDomain));
69+
return SchemaDefinitionsElectra.required(spec.atSlot(slot).getSchemaDefinitions())
70+
.getDepositReceiptSchema()
71+
.create(publicKey, Bytes32.ZERO, depositAmount, signature, index);
72+
}
73+
}

Diff for: ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerChannel.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public SafeFuture<ForkChoiceUpdatedResult> engineForkChoiceUpdated(
6060

6161
@Override
6262
public SafeFuture<GetPayloadResponse> engineGetPayload(
63-
final ExecutionPayloadContext executionPayloadContext, final UInt64 slot) {
63+
final ExecutionPayloadContext executionPayloadContext, final BeaconState state) {
6464
return SafeFuture.completedFuture(null);
6565
}
6666

@@ -123,7 +123,7 @@ SafeFuture<ForkChoiceUpdatedResult> engineForkChoiceUpdated(
123123
* BeaconState, boolean, Optional, BlockProductionPerformance)} instead
124124
*/
125125
SafeFuture<GetPayloadResponse> engineGetPayload(
126-
ExecutionPayloadContext executionPayloadContext, UInt64 slot);
126+
ExecutionPayloadContext executionPayloadContext, BeaconState state);
127127

128128
// builder namespace
129129
SafeFuture<Void> builderRegisterValidators(

Diff for: ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerChannelStub.java

+26-7
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@
6161
import tech.pegasys.teku.spec.datastructures.execution.HeaderWithFallbackData;
6262
import tech.pegasys.teku.spec.datastructures.execution.NewPayloadRequest;
6363
import tech.pegasys.teku.spec.datastructures.execution.PowBlock;
64+
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt;
6465
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
6566
import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment;
6667
import tech.pegasys.teku.spec.datastructures.util.BlobsUtil;
68+
import tech.pegasys.teku.spec.datastructures.util.DepositReceiptsUtil;
6769
import tech.pegasys.teku.spec.schemas.SchemaDefinitions;
6870
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsBellatrix;
6971
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb;
@@ -73,6 +75,8 @@ public class ExecutionLayerChannelStub implements ExecutionLayerChannel {
7375
private static final ClientVersion STUB_CLIENT_VERSION =
7476
new ClientVersion("SB", ExecutionLayerChannel.STUB_ENDPOINT_PREFIX, "0.0.0", Bytes4.ZERO);
7577

78+
private static final boolean GENERATE_DEPOSIT_RECEIPTS = false;
79+
7680
private final TimeProvider timeProvider;
7781
private final Map<Bytes32, PowBlock> knownBlocks = new ConcurrentHashMap<>();
7882
private final Map<Bytes32, PayloadStatus> knownPosBlocks = new ConcurrentHashMap<>();
@@ -81,6 +85,7 @@ public class ExecutionLayerChannelStub implements ExecutionLayerChannel {
8185
private final Set<Bytes32> requestedPowBlocks = new HashSet<>();
8286
private final Spec spec;
8387
private final BlobsUtil blobsUtil;
88+
private final DepositReceiptsUtil depositReceiptsUtil;
8489
private final Random random = new Random();
8590

8691
private PayloadStatus payloadStatus = PayloadStatus.VALID;
@@ -122,6 +127,7 @@ public ExecutionLayerChannelStub(
122127
kzg = KZG.NOOP;
123128
}
124129
this.blobsUtil = new BlobsUtil(spec, kzg);
130+
this.depositReceiptsUtil = new DepositReceiptsUtil(spec);
125131
}
126132

127133
public ExecutionLayerChannelStub(
@@ -222,14 +228,15 @@ public SafeFuture<ForkChoiceUpdatedResult> engineForkChoiceUpdated(
222228

223229
@Override
224230
public SafeFuture<GetPayloadResponse> engineGetPayload(
225-
final ExecutionPayloadContext executionPayloadContext, final UInt64 slot) {
231+
final ExecutionPayloadContext executionPayloadContext, final BeaconState state) {
226232
if (!bellatrixActivationDetected) {
227233
LOG.info(
228234
"getPayload received before terminalBlock has been sent. Assuming transition already happened");
229235

230236
// do the activation check to be able to respond to terminal block verification
231237
checkBellatrixActivation();
232238
}
239+
final UInt64 slot = state.getSlot();
233240

234241
final Optional<SchemaDefinitionsBellatrix> schemaDefinitionsBellatrix =
235242
spec.atSlot(slot).getSchemaDefinitions().toVersionBellatrix();
@@ -246,9 +253,7 @@ public SafeFuture<GetPayloadResponse> engineGetPayload(
246253
final List<Bytes> transactions = generateTransactions(slot, headAndAttrs);
247254

248255
final ExecutionPayload executionPayload =
249-
spec.atSlot(slot)
250-
.getSchemaDefinitions()
251-
.toVersionBellatrix()
256+
schemaDefinitionsBellatrix
252257
.orElseThrow()
253258
.getExecutionPayloadSchema()
254259
.createExecutionPayload(
@@ -271,7 +276,7 @@ public SafeFuture<GetPayloadResponse> engineGetPayload(
271276
.withdrawals(() -> payloadAttributes.getWithdrawals().orElse(List.of()))
272277
.blobGasUsed(() -> UInt64.ZERO)
273278
.excessBlobGas(() -> UInt64.ZERO)
274-
.depositReceipts(List::of)
279+
.depositReceipts(() -> generateDepositReceipts(state))
275280
.exits(List::of));
276281

277282
// we assume all blocks are produced locally
@@ -288,7 +293,7 @@ public SafeFuture<GetPayloadResponse> engineGetPayload(
288293
LOG.info(
289294
"getPayload: payloadId: {} slot: {} -> executionPayload blockHash: {}",
290295
executionPayloadContext.getPayloadId(),
291-
slot,
296+
state.getSlot(),
292297
executionPayload.getBlockHash());
293298

294299
final GetPayloadResponse getPayloadResponse =
@@ -346,7 +351,7 @@ public SafeFuture<HeaderWithFallbackData> builderGetHeader(
346351

347352
final SchemaDefinitions schemaDefinitions = spec.atSlot(slot).getSchemaDefinitions();
348353

349-
return engineGetPayload(executionPayloadContext, slot)
354+
return engineGetPayload(executionPayloadContext, state)
350355
.thenPeek(__ -> blockProductionPerformance.engineGetPayload())
351356
.thenApply(
352357
getPayloadResponse -> {
@@ -554,4 +559,18 @@ private Bytes generateBlobsAndTransaction(
554559

555560
return blobsUtil.generateRawBlobTransactionFromKzgCommitments(commitments);
556561
}
562+
563+
private List<DepositReceipt> generateDepositReceipts(final BeaconState state) {
564+
return spec.atSlot(state.getSlot())
565+
.getConfig()
566+
.toVersionElectra()
567+
.map(
568+
__ -> {
569+
if (GENERATE_DEPOSIT_RECEIPTS) {
570+
return depositReceiptsUtil.generateDepositReceipts(state);
571+
}
572+
return List.<DepositReceipt>of();
573+
})
574+
.orElse(List.of());
575+
}
557576
}

0 commit comments

Comments
 (0)