Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1014,9 +1014,10 @@ public SafeFuture<Optional<ExecutionPayloadEnvelope>> createUnsignedExecutionPay

@Override
public SafeFuture<PublishSignedExecutionPayloadResult> publishSignedExecutionPayload(
final SignedExecutionPayloadEnvelope signedExecutionPayload) {
final SignedExecutionPayloadEnvelope signedExecutionPayload,
final Optional<BroadcastValidationLevel> broadcastValidationLevel) {
return executionPayloadPublisher
.publishSignedExecutionPayload(signedExecutionPayload)
.publishSignedExecutionPayload(signedExecutionPayload, broadcastValidationLevel)
.exceptionally(
ex -> {
final String reason = getRootCauseMessage(ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,27 @@

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

import java.util.Optional;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.spec.datastructures.epbs.versions.gloas.SignedExecutionPayloadEnvelope;
import tech.pegasys.teku.spec.datastructures.validator.BroadcastValidationLevel;
import tech.pegasys.teku.validator.api.PublishSignedExecutionPayloadResult;

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

ExecutionPayloadPublisher NOOP =
signedExecutionPayload ->
(signedExecutionPayload, broadcastValidationLevel) ->
SafeFuture.completedFuture(
PublishSignedExecutionPayloadResult.success(
signedExecutionPayload.getBeaconBlockRoot()));

default SafeFuture<PublishSignedExecutionPayloadResult> publishSignedExecutionPayload(
final SignedExecutionPayloadEnvelope signedExecutionPayload) {
return publishSignedExecutionPayload(signedExecutionPayload, Optional.empty());
}

SafeFuture<PublishSignedExecutionPayloadResult> publishSignedExecutionPayload(
SignedExecutionPayloadEnvelope signedExecutionPayload);
SignedExecutionPayloadEnvelope signedExecutionPayload,
Optional<BroadcastValidationLevel> broadcastValidationLevel);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package tech.pegasys.teku.validator.coordinator.publisher;

import java.util.List;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes32;
Expand All @@ -22,6 +23,7 @@
import tech.pegasys.teku.networking.eth2.gossip.ExecutionPayloadGossipChannel;
import tech.pegasys.teku.spec.datastructures.blobs.DataColumnSidecar;
import tech.pegasys.teku.spec.datastructures.epbs.versions.gloas.SignedExecutionPayloadEnvelope;
import tech.pegasys.teku.spec.datastructures.validator.BroadcastValidationLevel;
import tech.pegasys.teku.statetransition.blobs.RemoteOrigin;
import tech.pegasys.teku.statetransition.execution.ExecutionPayloadManager;
import tech.pegasys.teku.validator.api.PublishSignedExecutionPayloadResult;
Expand Down Expand Up @@ -49,9 +51,11 @@ public ExecutionPayloadPublisherGloas(

@Override
public SafeFuture<PublishSignedExecutionPayloadResult> publishSignedExecutionPayload(
final SignedExecutionPayloadEnvelope signedExecutionPayload) {
final SignedExecutionPayloadEnvelope signedExecutionPayload,
final Optional<BroadcastValidationLevel> broadcastValidationLevel) {
return executionPayloadManager
.validateAndImportExecutionPayload(signedExecutionPayload)
.validateAndImportExecutionPayload(
signedExecutionPayload, Optional.empty(), broadcastValidationLevel)
.thenApply(
result -> {
final Bytes32 beaconBlockRoot = signedExecutionPayload.getBeaconBlockRoot();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
import tech.pegasys.teku.spec.datastructures.state.Checkpoint;
import tech.pegasys.teku.spec.datastructures.state.CheckpointState;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.datastructures.validator.BroadcastValidationLevel;
import tech.pegasys.teku.spec.logic.common.util.SyncCommitteeUtil;
import tech.pegasys.teku.spec.util.DataStructureUtil;
import tech.pegasys.teku.statetransition.attestation.AggregatingAttestationPool;
Expand Down Expand Up @@ -1375,13 +1376,38 @@ public void publishSignedExecutionPayload_shouldPublish() {
dataStructureUtil.randomSignedExecutionPayloadEnvelope(5);
final PublishSignedExecutionPayloadResult publishResult =
PublishSignedExecutionPayloadResult.success(signedExecutionPayload.getBeaconBlockRoot());
when(executionPayloadPublisher.publishSignedExecutionPayload(eq(signedExecutionPayload)))
when(executionPayloadPublisher.publishSignedExecutionPayload(
eq(signedExecutionPayload), eq(Optional.empty())))
.thenReturn(SafeFuture.completedFuture(publishResult));

assertThat(validatorApiHandler.publishSignedExecutionPayload(signedExecutionPayload))
assertThat(
validatorApiHandler.publishSignedExecutionPayload(
signedExecutionPayload, Optional.empty()))
.isCompletedWithValue(publishResult);

verify(executionPayloadPublisher).publishSignedExecutionPayload(signedExecutionPayload);
verify(executionPayloadPublisher)
.publishSignedExecutionPayload(signedExecutionPayload, Optional.empty());
}

@Test
public void publishSignedExecutionPayload_shouldPublishWithBroadcastValidationLevel() {
final SignedExecutionPayloadEnvelope signedExecutionPayload =
dataStructureUtil.randomSignedExecutionPayloadEnvelope(5);
final PublishSignedExecutionPayloadResult publishResult =
PublishSignedExecutionPayloadResult.success(signedExecutionPayload.getBeaconBlockRoot());
final Optional<BroadcastValidationLevel> broadcastValidationLevel = Optional.of(GOSSIP);

when(executionPayloadPublisher.publishSignedExecutionPayload(
eq(signedExecutionPayload), eq(broadcastValidationLevel)))
.thenReturn(SafeFuture.completedFuture(publishResult));

assertThat(
validatorApiHandler.publishSignedExecutionPayload(
signedExecutionPayload, broadcastValidationLevel))
.isCompletedWithValue(publishResult);

verify(executionPayloadPublisher)
.publishSignedExecutionPayload(signedExecutionPayload, broadcastValidationLevel);
}

@Test
Expand All @@ -1391,13 +1417,17 @@ public void publishSignedExecutionPayload_shouldHandleExceptions() {
final PublishSignedExecutionPayloadResult failedResult =
PublishSignedExecutionPayloadResult.rejected(
signedExecutionPayload.getBeaconBlockRoot(), "oopsy");
when(executionPayloadPublisher.publishSignedExecutionPayload(eq(signedExecutionPayload)))
when(executionPayloadPublisher.publishSignedExecutionPayload(
eq(signedExecutionPayload), eq(Optional.empty())))
.thenReturn(SafeFuture.failedFuture(new IllegalStateException("oopsy")));

assertThat(validatorApiHandler.publishSignedExecutionPayload(signedExecutionPayload))
assertThat(
validatorApiHandler.publishSignedExecutionPayload(
signedExecutionPayload, Optional.empty()))
.isCompletedWithValue(failedResult);

verify(executionPayloadPublisher).publishSignedExecutionPayload(signedExecutionPayload);
verify(executionPayloadPublisher)
.publishSignedExecutionPayload(signedExecutionPayload, Optional.empty());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@

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

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;

import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
Expand Down Expand Up @@ -65,7 +68,8 @@ class ExecutionPayloadPublisherGloasTest {

@BeforeEach
public void setUp() {
when(executionPayloadManager.validateAndImportExecutionPayload(signedExecutionPayload))
when(executionPayloadManager.validateAndImportExecutionPayload(
eq(signedExecutionPayload), eq(Optional.empty()), any()))
.thenReturn(SafeFuture.completedFuture(InternalValidationResult.ACCEPT));
when(executionPayloadFactory.createDataColumnSidecars(signedExecutionPayload))
.thenReturn(SafeFuture.completedFuture(dataColumnSidecars));
Expand All @@ -88,7 +92,8 @@ public void publishSignedExecutionPayload_shouldValidateAndPublish() {

@Test
public void publishSignedExecutionPayload_shouldReturnRejectedResultIfGossipValidationFails() {
when(executionPayloadManager.validateAndImportExecutionPayload(signedExecutionPayload))
when(executionPayloadManager.validateAndImportExecutionPayload(
eq(signedExecutionPayload), eq(Optional.empty()), any()))
.thenReturn(SafeFuture.completedFuture(InternalValidationResult.reject("oopsy")));
SafeFutureAssert.assertThatSafeFuture(
executionPayloadPublisher.publishSignedExecutionPayload(signedExecutionPayload))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"post" : {
"tags" : [ "Beacon", "Validator Required Api" ],
"operationId" : "publishExecutionPayloadEnvelope",
"summary" : "Publish signed execution payload envelope",
"description" : "Instructs the beacon node to broadcast a signed execution payload envelope to the network, to be gossiped for payload validation.",
"parameters" : [ {
"name" : "broadcast_validation",
"in" : "query",
"schema" : {
"type" : "string",
"description" : "Level of validation that must be applied to a block before it is broadcast. Possible values:\n- **`gossip`** (default): lightweight gossip checks only\n- **`consensus`**: full consensus checks, including validation of all signatures and blocks fields _except_ for the execution payload transactions.\n- **`consensus_and_equivocation`**: the same as `consensus`, with an extra equivocation check immediately before the block is broadcast. If the block is found to be an\n equivocation it fails validation.\nIf the block fails the requested level of a validation a 400 status MUST be returned immediately and the block MUST NOT be broadcast to the network.\nIf validation succeeds, the block must still be fully verified before it is incorporated into the state and a 20x status is returned to the caller.",
"example" : "consensus_and_equivocation",
"format" : "string",
"enum" : [ "gossip", "consensus", "consensus_and_equivocation" ]
}
}, {
"name" : "Eth-Consensus-Version",
"required" : true,
"in" : "header",
"schema" : {
"type" : "string",
"enum" : [ "phase0", "altair", "bellatrix", "capella", "deneb", "electra", "fulu", "gloas", "heze" ],
"description" : "Version of the block being submitted, if using SSZ encoding."
}
} ],
"requestBody" : {
"content" : {
"application/octet-stream" : {
"schema" : {
"type" : "string",
"format" : "binary"
}
},
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/SignedExecutionPayloadEnvelope"
}
}
}
},
"responses" : {
"415" : {
"description" : "Unsupported media type",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
},
"200" : {
"description" : "The envelope was validated successfully and has been broadcast. It has also been integrated into the beacon node's database.",
"content" : { }
},
"202" : {
"description" : "The envelope could not be integrated into the beacon node's database as it failed validation, but was successfully broadcast.",
"content" : { }
},
"400" : {
"description" : "The SignedExecutionPayloadEnvelope object is invalid or broadcast validation failed",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
},
"500" : {
"description" : "Internal server error",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"in" : "query",
"schema" : {
"type" : "string",
"description" : "Level of validation that must be applied to a block before it is broadcast. Possible values:\n- **`gossip`** (default): lightweight gossip checks only\n- **`consensus`**: full consensus checks, including validation of all signatures and blocks fields _except_ for the execution payload transactions.\n- **`consensus_and_equivocation`**: the same as `consensus`, with an extra equivocation check immediately before the block is broadcast. If the block is found to be an\n equivocation it fails validation.\nIf the block fails the requested level of a validation a 400 status MUST be returned immediately and the block MUST NOT be broadcast to the network.\nIf validation succeeds, the block must still be fully verified before it is incorporated into the state and a 20x status is returned to the caller.",
"description" : "Level of validation that must be applied to a block before it is broadcast. Possible values:\n- **`gossip`** (default): lightweight gossip checks only\n- **`consensus`**: full consensus checks, including validation of all signatures and blocks fields _except_ for the execution payload transactions.\n- **`consensus_and_equivocation`**: the same as `consensus`, with an extra equivocation check immediately before the block is broadcast. If the block is found to be an\n equivocation it fails validation.\nIf the block fails the requested level of a validation a 400 status MUST be returned immediately and the block MUST NOT be broadcast to the network.\nIf validation succeeds, the block must still be fully verified before it is incorporated into the state and a 20x status is returned to the caller.",
"example" : "consensus_and_equivocation",
"format" : "string",
"enum" : [ "gossip", "consensus", "consensus_and_equivocation" ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"in" : "query",
"schema" : {
"type" : "string",
"description" : "Level of validation that must be applied to a block before it is broadcast. Possible values:\n- **`gossip`** (default): lightweight gossip checks only\n- **`consensus`**: full consensus checks, including validation of all signatures and blocks fields _except_ for the execution payload transactions.\n- **`consensus_and_equivocation`**: the same as `consensus`, with an extra equivocation check immediately before the block is broadcast. If the block is found to be an\n equivocation it fails validation.\nIf the block fails the requested level of a validation a 400 status MUST be returned immediately and the block MUST NOT be broadcast to the network.\nIf validation succeeds, the block must still be fully verified before it is incorporated into the state and a 20x status is returned to the caller.",
"description" : "Level of validation that must be applied to a block before it is broadcast. Possible values:\n- **`gossip`** (default): lightweight gossip checks only\n- **`consensus`**: full consensus checks, including validation of all signatures and blocks fields _except_ for the execution payload transactions.\n- **`consensus_and_equivocation`**: the same as `consensus`, with an extra equivocation check immediately before the block is broadcast. If the block is found to be an\n equivocation it fails validation.\nIf the block fails the requested level of a validation a 400 status MUST be returned immediately and the block MUST NOT be broadcast to the network.\nIf validation succeeds, the block must still be fully verified before it is incorporated into the state and a 20x status is returned to the caller.",
"example" : "consensus_and_equivocation",
"format" : "string",
"enum" : [ "gossip", "consensus", "consensus_and_equivocation" ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import tech.pegasys.teku.beaconrestapi.RestApiBuilderAddon;
import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetExecutionPayloadEnvelope;
import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetPayloadAttestations;
import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.PostExecutionPayloadEnvelope;
import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.PostPublishExecutionPayloadBid;
import tech.pegasys.teku.beaconrestapi.handlers.v1.validator.GetExecutionPayloadBid;
import tech.pegasys.teku.infrastructure.restapi.RestApiBuilder;
Expand Down Expand Up @@ -47,6 +48,8 @@ public RestApiBuilder apply(final RestApiBuilder builder) {
return builder
.endpoint(new GetExecutionPayloadEnvelope(dataProvider, schemaCache))
.endpoint(new GetPayloadAttestations(dataProvider, schemaCache))
.endpoint(
new PostExecutionPayloadEnvelope(dataProvider.getValidatorDataProvider(), schemaCache))
.endpoint(new GetExecutionPayloadBid(dataProvider, schemaCache))
.endpoint(new PostPublishExecutionPayloadBid(dataProvider, schemaCache));
}
Expand Down
Loading
Loading