From bb895e2025d3f95ad8d9563b22fe52ffc35e7c8d Mon Sep 17 00:00:00 2001 From: Mehdi AOUADI Date: Thu, 2 Nov 2023 20:43:34 +0100 Subject: [PATCH] 7390 block v2 fallback (#7637) * Add block v2 fallback --- .../logic/common/util/BlockProposalUtil.java | 7 ++- .../logic/common/util/ValidatorsUtil.java | 1 + .../client/duties/BlockProductionDuty.java | 2 +- .../OkHttpValidatorTypeDefClientTest.java | 45 ++++++++++++++++++- .../BlockProductionV3FailedException.java | 16 +++++++ .../typedef/OkHttpValidatorTypeDefClient.java | 9 +++- .../typedef/handlers/ProduceBlockRequest.java | 10 ++++- 7 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/BlockProductionV3FailedException.java diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/BlockProposalUtil.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/BlockProposalUtil.java index 2516e601c8c..3c8bd397159 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/BlockProposalUtil.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/BlockProposalUtil.java @@ -117,7 +117,12 @@ public SafeFuture createNewUnsignedBlock( final Bytes32 parentBlockSigningRoot, final Consumer bodyBuilder) { return createNewUnsignedBlock( - newSlot, proposerIndex, blockSlotState, parentBlockSigningRoot, bodyBuilder, true); + newSlot, + proposerIndex, + blockSlotState, + parentBlockSigningRoot, + bodyBuilder, + ValidatorsUtil.DEFAULT_PRODUCE_BLINDED_BLOCK); } private SafeFuture createBeaconBlockBody( diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/ValidatorsUtil.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/ValidatorsUtil.java index f030d7d99b8..791fbdffd9f 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/ValidatorsUtil.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/ValidatorsUtil.java @@ -35,6 +35,7 @@ public class ValidatorsUtil { + public static final boolean DEFAULT_PRODUCE_BLINDED_BLOCK = true; private final SpecConfig specConfig; private final MiscHelpers miscHelpers; private final BeaconStateAccessors beaconStateAccessors; diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/duties/BlockProductionDuty.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/duties/BlockProductionDuty.java index 5a0496cb994..c4ac613e213 100644 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/duties/BlockProductionDuty.java +++ b/validator/client/src/main/java/tech/pegasys/teku/validator/client/duties/BlockProductionDuty.java @@ -103,7 +103,7 @@ private SafeFuture createRandaoReveal(final ForkInfo forkInfo) { private SafeFuture> createUnsignedBlock( final BLSSignature randaoReveal) { - if (this.blockV3Enabled) { + if (blockV3Enabled) { return validatorApiChannel.createUnsignedBlock(slot, randaoReveal, validator.getGraffiti()); } else { return validatorApiChannel.createUnsignedBlock( diff --git a/validator/remote/src/integration-test/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClientTest.java b/validator/remote/src/integration-test/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClientTest.java index 19e2697626b..a578b4f2e35 100644 --- a/validator/remote/src/integration-test/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClientTest.java +++ b/validator/remote/src/integration-test/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClientTest.java @@ -15,6 +15,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assumptions.assumeThat; +import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ONE; +import static tech.pegasys.teku.spec.SpecMilestone.DENEB; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -296,6 +298,44 @@ void registerValidators_fallbacksToJsonIfSszNotSupported() throws InterruptedExc verifyRegisterValidatorsPostRequest(mockWebServer.takeRequest(), JSON_CONTENT_TYPE); } + @TestTemplate + void blockV3ShouldFallbacksToBlockV2WhenNotFound() + throws JsonProcessingException, InterruptedException { + mockWebServer.enqueue(new MockResponse().setResponseCode(404)); + + final BlockContainer blockContainer; + if (specMilestone.isGreaterThanOrEqualTo(DENEB)) { + blockContainer = dataStructureUtil.randomBlindedBlockContents(ONE); + } else { + blockContainer = dataStructureUtil.randomBlindedBeaconBlock(ONE); + } + + mockWebServer.enqueue( + new MockResponse() + .setResponseCode(200) + .setBody( + "{\"data\": " + + serializeBlockContainer(blockContainer) + + ", \"version\": \"" + + specMilestone + + "\"}")); + + final Optional producedBlock = + okHttpValidatorTypeDefClient.createUnsignedBlock( + dataStructureUtil.randomUInt64(), + dataStructureUtil.randomSignature(), + Optional.empty()); + + assertThat(producedBlock).hasValue(blockContainer); + + assertThat(mockWebServer.getRequestCount()).isEqualTo(2); + + final RecordedRequest firstRequest = mockWebServer.takeRequest(); + assertThat(firstRequest.getPath()).startsWith("/eth/v3/validator/blocks"); + final RecordedRequest secondRequest = mockWebServer.takeRequest(); + assertThat(secondRequest.getPath()).startsWith("/eth/v1/validator/blinded_blocks"); + } + private void verifyRegisterValidatorsPostRequest( final RecordedRequest recordedRequest, final String expectedContentType) { assertThat(recordedRequest.getPath()).isEqualTo("/eth/v1/validator/register_validator"); @@ -315,6 +355,9 @@ private void assertJsonEquals(final String actual, final String expected) { private String serializeBlockContainer(final BlockContainer blockContainer) throws JsonProcessingException { return JsonUtil.serialize( - blockContainer, schemaDefinitions.getBlockContainerSchema().getJsonTypeDefinition()); + blockContainer, + blockContainer.isBlinded() + ? schemaDefinitions.getBlindedBlockContainerSchema().getJsonTypeDefinition() + : schemaDefinitions.getBlockContainerSchema().getJsonTypeDefinition()); } } diff --git a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/BlockProductionV3FailedException.java b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/BlockProductionV3FailedException.java new file mode 100644 index 00000000000..17b389b947a --- /dev/null +++ b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/BlockProductionV3FailedException.java @@ -0,0 +1,16 @@ +/* + * Copyright Consensys Software Inc., 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package tech.pegasys.teku.validator.remote.typedef; + +public class BlockProductionV3FailedException extends RuntimeException {} diff --git a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClient.java b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClient.java index 4166014b1b1..228c50cbf12 100644 --- a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClient.java +++ b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClient.java @@ -28,6 +28,7 @@ import tech.pegasys.teku.spec.datastructures.builder.SignedValidatorRegistration; import tech.pegasys.teku.spec.datastructures.genesis.GenesisData; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; +import tech.pegasys.teku.spec.logic.common.util.ValidatorsUtil; import tech.pegasys.teku.validator.api.SendSignedBlockResult; import tech.pegasys.teku.validator.api.required.SyncingStatus; import tech.pegasys.teku.validator.remote.typedef.handlers.CreateAttestationDataRequest; @@ -111,7 +112,13 @@ public Optional createUnsignedBlock( final UInt64 slot, final BLSSignature randaoReveal, final Optional graffiti) { final ProduceBlockRequest produceBlockRequest = new ProduceBlockRequest(baseEndpoint, okHttpClient, spec, slot, preferSszBlockEncoding); - return produceBlockRequest.createUnsignedBlock(randaoReveal, graffiti); + try { + return produceBlockRequest.createUnsignedBlock(randaoReveal, graffiti); + } catch (final BlockProductionV3FailedException ex) { + LOG.warn("Produce Block V3 request failed at slot {}. Retrying with Block V2", slot); + return createUnsignedBlock( + slot, randaoReveal, graffiti, ValidatorsUtil.DEFAULT_PRODUCE_BLINDED_BLOCK); + } } public void registerValidators( diff --git a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/ProduceBlockRequest.java b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/ProduceBlockRequest.java index 89701956c0d..6adfb2e6c55 100644 --- a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/ProduceBlockRequest.java +++ b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/ProduceBlockRequest.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.validator.remote.typedef.handlers; +import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND; import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.CONSENSUS_BLOCK_VALUE; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.EXECUTION_PAYLOAD_BLINDED; @@ -45,6 +46,7 @@ import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.datastructures.blocks.BlockContainer; import tech.pegasys.teku.spec.datastructures.blocks.BlockContainerSchema; +import tech.pegasys.teku.validator.remote.typedef.BlockProductionV3FailedException; import tech.pegasys.teku.validator.remote.typedef.ResponseHandler; public class ProduceBlockRequest extends AbstractTypeDefRequest { @@ -56,7 +58,6 @@ public class ProduceBlockRequest extends AbstractTypeDefRequest { private final BlockContainerSchema blockContainerSchema; private final BlockContainerSchema blindedBlockContainerSchema; private final ResponseHandler responseHandler; - public final DeserializableOneOfTypeDefinition produceBlockTypeDefinition; public ProduceBlockRequest( @@ -91,7 +92,12 @@ public ProduceBlockRequest( this.responseHandler = new ResponseHandler<>(produceBlockTypeDefinition) - .withHandler(SC_OK, this::handleBlockContainerResult); + .withHandler(SC_OK, this::handleBlockContainerResult) + .withHandler( + SC_NOT_FOUND, + (__, ___) -> { + throw new BlockProductionV3FailedException(); + }); } public Optional createUnsignedBlock(