Skip to content

Commit ae6918b

Browse files
Return a result object instead of void when publishing execution payload (#10158)
1 parent 0b64d2a commit ae6918b

File tree

31 files changed

+368
-139
lines changed

31 files changed

+368
-139
lines changed

beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandler.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
import tech.pegasys.teku.storage.client.CombinedChainDataClient;
116116
import tech.pegasys.teku.validator.api.CommitteeSubscriptionRequest;
117117
import tech.pegasys.teku.validator.api.NodeSyncingException;
118+
import tech.pegasys.teku.validator.api.PublishSignedExecutionPayloadResult;
118119
import tech.pegasys.teku.validator.api.SendSignedBlockResult;
119120
import tech.pegasys.teku.validator.api.SubmitDataError;
120121
import tech.pegasys.teku.validator.api.ValidatorApiChannel;
@@ -987,9 +988,16 @@ public SafeFuture<Optional<ExecutionPayloadEnvelope>> createUnsignedExecutionPay
987988
}
988989

989990
@Override
990-
public SafeFuture<Void> publishSignedExecutionPayload(
991+
public SafeFuture<PublishSignedExecutionPayloadResult> publishSignedExecutionPayload(
991992
final SignedExecutionPayloadEnvelope signedExecutionPayload) {
992-
return executionPayloadPublisher.publishSignedExecutionPayload(signedExecutionPayload);
993+
return executionPayloadPublisher
994+
.publishSignedExecutionPayload(signedExecutionPayload)
995+
.exceptionally(
996+
ex -> {
997+
final String reason = getRootCauseMessage(ex);
998+
return PublishSignedExecutionPayloadResult.rejected(
999+
signedExecutionPayload.getBeaconBlockRoot(), reason);
1000+
});
9931001
}
9941002

9951003
private Optional<SubmitDataError> fromInternalValidationResult(

beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/ExecutionPayloadPublisher.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,17 @@
1515

1616
import tech.pegasys.teku.infrastructure.async.SafeFuture;
1717
import tech.pegasys.teku.spec.datastructures.epbs.versions.gloas.SignedExecutionPayloadEnvelope;
18+
import tech.pegasys.teku.validator.api.PublishSignedExecutionPayloadResult;
1819

1920
/** Used to publish execution payload and data column sidecars */
2021
public interface ExecutionPayloadPublisher {
2122

2223
ExecutionPayloadPublisher NOOP =
23-
new ExecutionPayloadPublisher() {
24-
@Override
25-
public SafeFuture<Void> publishSignedExecutionPayload(
26-
final SignedExecutionPayloadEnvelope signedExecutionPayload) {
27-
return SafeFuture.COMPLETE;
28-
}
29-
};
24+
signedExecutionPayload ->
25+
SafeFuture.completedFuture(
26+
PublishSignedExecutionPayloadResult.success(
27+
signedExecutionPayload.getBeaconBlockRoot()));
3028

31-
SafeFuture<Void> publishSignedExecutionPayload(
29+
SafeFuture<PublishSignedExecutionPayloadResult> publishSignedExecutionPayload(
3230
SignedExecutionPayloadEnvelope signedExecutionPayload);
3331
}

beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/ExecutionPayloadPublisherGloas.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
import java.util.List;
1717
import org.apache.logging.log4j.LogManager;
1818
import org.apache.logging.log4j.Logger;
19+
import org.apache.tuweni.bytes.Bytes32;
1920
import tech.pegasys.teku.infrastructure.async.SafeFuture;
2021
import tech.pegasys.teku.networking.eth2.gossip.DataColumnSidecarGossipChannel;
2122
import tech.pegasys.teku.networking.eth2.gossip.ExecutionPayloadGossipChannel;
2223
import tech.pegasys.teku.spec.datastructures.blobs.DataColumnSidecar;
2324
import tech.pegasys.teku.spec.datastructures.epbs.versions.gloas.SignedExecutionPayloadEnvelope;
2425
import tech.pegasys.teku.statetransition.blobs.RemoteOrigin;
2526
import tech.pegasys.teku.statetransition.execution.ExecutionPayloadManager;
27+
import tech.pegasys.teku.validator.api.PublishSignedExecutionPayloadResult;
2628
import tech.pegasys.teku.validator.coordinator.ExecutionPayloadFactory;
2729

2830
public class ExecutionPayloadPublisherGloas implements ExecutionPayloadPublisher {
@@ -46,13 +48,26 @@ public ExecutionPayloadPublisherGloas(
4648
}
4749

4850
@Override
49-
public SafeFuture<Void> publishSignedExecutionPayload(
51+
public SafeFuture<PublishSignedExecutionPayloadResult> publishSignedExecutionPayload(
5052
final SignedExecutionPayloadEnvelope signedExecutionPayload) {
51-
// we publish the execution payload (and data column sidecars) immediately and then import
52-
publishExecutionPayloadAndDataColumnSidecars(
53-
signedExecutionPayload,
54-
executionPayloadFactory.createDataColumnSidecars(signedExecutionPayload));
55-
return executionPayloadManager.importExecutionPayload(signedExecutionPayload).toVoid();
53+
return executionPayloadManager
54+
.validateAndImportExecutionPayload(signedExecutionPayload)
55+
.thenApply(
56+
result -> {
57+
final Bytes32 beaconBlockRoot = signedExecutionPayload.getBeaconBlockRoot();
58+
if (result.isAccept()) {
59+
// we publish the execution payload (and data column sidecars) after passing gossip
60+
// validation
61+
publishExecutionPayloadAndDataColumnSidecars(
62+
signedExecutionPayload,
63+
executionPayloadFactory.createDataColumnSidecars(signedExecutionPayload));
64+
return PublishSignedExecutionPayloadResult.success(beaconBlockRoot);
65+
}
66+
return PublishSignedExecutionPayloadResult.rejected(
67+
beaconBlockRoot,
68+
"Failed gossip validation"
69+
+ result.getDescription().map(description -> ": " + description).orElse(""));
70+
});
5671
}
5772

5873
private void publishExecutionPayloadAndDataColumnSidecars(

beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
import tech.pegasys.teku.storage.client.CombinedChainDataClient;
124124
import tech.pegasys.teku.validator.api.CommitteeSubscriptionRequest;
125125
import tech.pegasys.teku.validator.api.NodeSyncingException;
126+
import tech.pegasys.teku.validator.api.PublishSignedExecutionPayloadResult;
126127
import tech.pegasys.teku.validator.api.SendSignedBlockResult;
127128
import tech.pegasys.teku.validator.api.SubmitDataError;
128129
import tech.pegasys.teku.validator.coordinator.performance.DefaultPerformanceTracker;
@@ -1340,12 +1341,29 @@ public void createUnsignedExecutionPayload_shouldCreateExecutionPayload() {
13401341
public void publishSignedExecutionPayload_shouldPublish() {
13411342
final SignedExecutionPayloadEnvelope signedExecutionPayload =
13421343
dataStructureUtil.randomSignedExecutionPayloadEnvelope(5);
1344+
final PublishSignedExecutionPayloadResult publishResult =
1345+
PublishSignedExecutionPayloadResult.success(signedExecutionPayload.getBeaconBlockRoot());
13431346
when(executionPayloadPublisher.publishSignedExecutionPayload(eq(signedExecutionPayload)))
1344-
.thenReturn(SafeFuture.COMPLETE);
1345-
final SafeFuture<Void> result =
1346-
validatorApiHandler.publishSignedExecutionPayload(signedExecutionPayload);
1347+
.thenReturn(SafeFuture.completedFuture(publishResult));
13471348

1348-
assertThat(result).isCompleted();
1349+
assertThat(validatorApiHandler.publishSignedExecutionPayload(signedExecutionPayload))
1350+
.isCompletedWithValue(publishResult);
1351+
1352+
verify(executionPayloadPublisher).publishSignedExecutionPayload(signedExecutionPayload);
1353+
}
1354+
1355+
@Test
1356+
public void publishSignedExecutionPayload_shouldHandleExceptions() {
1357+
final SignedExecutionPayloadEnvelope signedExecutionPayload =
1358+
dataStructureUtil.randomSignedExecutionPayloadEnvelope(5);
1359+
final PublishSignedExecutionPayloadResult failedResult =
1360+
PublishSignedExecutionPayloadResult.rejected(
1361+
signedExecutionPayload.getBeaconBlockRoot(), "oopsy");
1362+
when(executionPayloadPublisher.publishSignedExecutionPayload(eq(signedExecutionPayload)))
1363+
.thenReturn(SafeFuture.failedFuture(new IllegalStateException("oopsy")));
1364+
1365+
assertThat(validatorApiHandler.publishSignedExecutionPayload(signedExecutionPayload))
1366+
.isCompletedWithValue(failedResult);
13491367

13501368
verify(executionPayloadPublisher).publishSignedExecutionPayload(signedExecutionPayload);
13511369
}

beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/publisher/ExecutionPayloadPublisherGloasTest.java

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313

1414
package tech.pegasys.teku.validator.coordinator.publisher;
1515

16-
import static org.mockito.Mockito.inOrder;
1716
import static org.mockito.Mockito.mock;
17+
import static org.mockito.Mockito.verify;
18+
import static org.mockito.Mockito.verifyNoInteractions;
1819
import static org.mockito.Mockito.when;
1920

2021
import java.util.List;
2122
import org.junit.jupiter.api.BeforeEach;
2223
import org.junit.jupiter.api.Test;
23-
import org.mockito.InOrder;
2424
import tech.pegasys.teku.infrastructure.async.SafeFuture;
2525
import tech.pegasys.teku.infrastructure.async.SafeFutureAssert;
2626
import tech.pegasys.teku.networking.eth2.gossip.DataColumnSidecarGossipChannel;
@@ -29,10 +29,11 @@
2929
import tech.pegasys.teku.spec.TestSpecFactory;
3030
import tech.pegasys.teku.spec.datastructures.blobs.DataColumnSidecar;
3131
import tech.pegasys.teku.spec.datastructures.epbs.versions.gloas.SignedExecutionPayloadEnvelope;
32-
import tech.pegasys.teku.spec.logic.common.statetransition.results.ExecutionPayloadImportResult;
3332
import tech.pegasys.teku.spec.util.DataStructureUtil;
3433
import tech.pegasys.teku.statetransition.blobs.RemoteOrigin;
3534
import tech.pegasys.teku.statetransition.execution.ExecutionPayloadManager;
35+
import tech.pegasys.teku.statetransition.validation.InternalValidationResult;
36+
import tech.pegasys.teku.validator.api.PublishSignedExecutionPayloadResult;
3637
import tech.pegasys.teku.validator.coordinator.ExecutionPayloadFactory;
3738

3839
class ExecutionPayloadPublisherGloasTest {
@@ -64,30 +65,37 @@ class ExecutionPayloadPublisherGloasTest {
6465

6566
@BeforeEach
6667
public void setUp() {
68+
when(executionPayloadManager.validateAndImportExecutionPayload(signedExecutionPayload))
69+
.thenReturn(SafeFuture.completedFuture(InternalValidationResult.ACCEPT));
6770
when(executionPayloadFactory.createDataColumnSidecars(signedExecutionPayload))
6871
.thenReturn(SafeFuture.completedFuture(dataColumnSidecars));
6972
when(executionPayloadGossipChannel.publishExecutionPayload(signedExecutionPayload))
7073
.thenReturn(SafeFuture.COMPLETE);
71-
when(executionPayloadManager.importExecutionPayload(signedExecutionPayload))
72-
.thenReturn(
73-
SafeFuture.completedFuture(
74-
ExecutionPayloadImportResult.successful(signedExecutionPayload)));
7574
}
7675

7776
@Test
78-
public void publishSignedExecutionPayload_shouldPublishImmediatelyAndImport() {
77+
public void publishSignedExecutionPayload_shouldValidateAndPublish() {
7978
SafeFutureAssert.assertThatSafeFuture(
8079
executionPayloadPublisher.publishSignedExecutionPayload(signedExecutionPayload))
81-
.isCompleted();
80+
.isCompletedWithValue(
81+
PublishSignedExecutionPayloadResult.success(
82+
signedExecutionPayload.getBeaconBlockRoot()));
8283

83-
final InOrder inOrder =
84-
inOrder(
85-
executionPayloadGossipChannel, dataColumnSidecarGossipChannel, executionPayloadManager);
86-
87-
inOrder.verify(executionPayloadGossipChannel).publishExecutionPayload(signedExecutionPayload);
88-
inOrder
89-
.verify(dataColumnSidecarGossipChannel)
84+
verify(executionPayloadGossipChannel).publishExecutionPayload(signedExecutionPayload);
85+
verify(dataColumnSidecarGossipChannel)
9086
.publishDataColumnSidecars(dataColumnSidecars, RemoteOrigin.LOCAL_PROPOSAL);
91-
inOrder.verify(executionPayloadManager).importExecutionPayload(signedExecutionPayload);
87+
}
88+
89+
@Test
90+
public void publishSignedExecutionPayload_shouldReturnRejectedResultIfGossipValidationFails() {
91+
when(executionPayloadManager.validateAndImportExecutionPayload(signedExecutionPayload))
92+
.thenReturn(SafeFuture.completedFuture(InternalValidationResult.reject("oopsy")));
93+
SafeFutureAssert.assertThatSafeFuture(
94+
executionPayloadPublisher.publishSignedExecutionPayload(signedExecutionPayload))
95+
.isCompletedWithValue(
96+
PublishSignedExecutionPayloadResult.rejected(
97+
signedExecutionPayload.getBeaconBlockRoot(), "Failed gossip validation: oopsy"));
98+
99+
verifyNoInteractions(executionPayloadGossipChannel, dataColumnSidecarGossipChannel);
92100
}
93101
}

ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/epbs/versions/gloas/SignedExecutionPayloadEnvelope.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
package tech.pegasys.teku.spec.datastructures.epbs.versions.gloas;
1515

16+
import org.apache.tuweni.bytes.Bytes32;
1617
import tech.pegasys.teku.bls.BLSSignature;
1718
import tech.pegasys.teku.infrastructure.logging.LogFormatter;
1819
import tech.pegasys.teku.infrastructure.ssz.containers.Container2;
@@ -53,6 +54,10 @@ public UInt64 getSlot() {
5354
return getMessage().getSlot();
5455
}
5556

57+
public Bytes32 getBeaconBlockRoot() {
58+
return getMessage().getBeaconBlockRoot();
59+
}
60+
5661
public SlotAndBlockRoot getSlotAndBlockRoot() {
5762
return getMessage().getSlotAndBlockRoot();
5863
}

ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/forkchoice/MutableStore.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
2323
import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState;
2424
import tech.pegasys.teku.spec.datastructures.blocks.SlotAndBlockRoot;
25+
import tech.pegasys.teku.spec.datastructures.epbs.versions.gloas.SignedExecutionPayloadEnvelope;
2526
import tech.pegasys.teku.spec.datastructures.state.Checkpoint;
2627
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
2728

@@ -67,6 +68,15 @@ default void putBlockAndState(
6768
Optional.empty());
6869
}
6970

71+
/**
72+
* Stores the corresponding data for this execution payload
73+
*
74+
* @param executionPayload Execution payload
75+
* @param state Corresponding state
76+
*/
77+
void putExecutionPayloadAndState(
78+
SignedExecutionPayloadEnvelope executionPayload, BeaconState state);
79+
7080
void putStateRoot(Bytes32 stateRoot, SlotAndBlockRoot slotAndBlockRoot);
7181

7282
void pullUpBlockCheckpoints(Bytes32 blockRoot);

ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/execution/AbstractExecutionPayloadProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public BeaconState processAndVerifyExecutionPayload(
4747
"State transition error while importing execution payload (builder index: %s, slot: %s, block root: %s)",
4848
signedEnvelope.getMessage().getBuilderIndex(),
4949
signedEnvelope.getMessage().getSlot(),
50-
signedEnvelope.getMessage().getBeaconBlockRoot()),
50+
signedEnvelope.getBeaconBlockRoot()),
5151
ex);
5252
throw new StateTransitionException(ex);
5353
}

ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/ForkChoiceUtil.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,4 +548,9 @@ private boolean isExecutionBlock(final ReadOnlyStore store, final SignedBeaconBl
548548
public AvailabilityChecker<?> createAvailabilityChecker(final SignedBeaconBlock block) {
549549
return AvailabilityChecker.NOOP;
550550
}
551+
552+
public AvailabilityChecker<?> createAvailabilityChecker(
553+
final SignedExecutionPayloadEnvelope executionPayload) {
554+
return AvailabilityChecker.NOOP;
555+
}
551556
}

ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/gloas/util/ForkChoiceUtilGloas.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,21 @@ public void applyExecutionPayloadToStore(
4040
final MutableStore store,
4141
final SignedExecutionPayloadEnvelope signedEnvelope,
4242
final BeaconState postState) {
43-
// TODO-GLOAS: https://github.com/Consensys/teku/issues/9878
43+
// Add new execution payload to store
44+
store.putExecutionPayloadAndState(signedEnvelope, postState);
4445
}
4546

47+
// Checking of blob data availability is delayed until the processing of the execution payload
4648
@Override
4749
public AvailabilityChecker<?> createAvailabilityChecker(final SignedBeaconBlock block) {
48-
// TODO-GLOAS: in ePBS, data availability is delayed until the processing of the execution
49-
// payload.
50-
// We may have a dedicated availability checker for the execution stage.
51-
// If it will be the case, this will remain a NOOP
52-
return AvailabilityChecker.NOOP;
50+
return AvailabilityChecker.NOOP_DATACOLUMN_SIDECAR;
51+
}
52+
53+
// TODO-GLOAS: https://github.com/Consensys/teku/issues/9878 add a real data availability check
54+
// (not required for devnet-0)
55+
@Override
56+
public AvailabilityChecker<?> createAvailabilityChecker(
57+
final SignedExecutionPayloadEnvelope executionPayload) {
58+
return AvailabilityChecker.NOOP_DATACOLUMN_SIDECAR;
5359
}
5460
}

0 commit comments

Comments
 (0)