Skip to content

Commit

Permalink
7390 deserialisation (Consensys#7549)
Browse files Browse the repository at this point in the history
* add RemoteValidatorApiHandler Block V3 implementation
  • Loading branch information
mehdi-aouadi authored Oct 26, 2023
1 parent 0ade8dc commit 30d8007
Show file tree
Hide file tree
Showing 18 changed files with 537 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ void shouldGetUnblindedBeaconBlockAsSsz() throws IOException {
@TestTemplate
void shouldGetBlindedBeaconBlockAsJson() throws IOException {
assumeThat(specMilestone).isGreaterThanOrEqualTo(BELLATRIX).isLessThanOrEqualTo(CAPELLA);
final BeaconBlock beaconBlock = dataStructureUtil.randomBlindedBeaconBlock(ONE);
final BLSSignature signature = beaconBlock.getBlock().getBody().getRandaoReveal();
final BeaconBlock blindedBeaconBlock = dataStructureUtil.randomBlindedBeaconBlock(ONE);
final BLSSignature signature = blindedBeaconBlock.getBlock().getBody().getRandaoReveal();
when(validatorApiChannel.createUnsignedBlock(eq(UInt64.ONE), eq(signature), any()))
.thenReturn(SafeFuture.completedFuture(Optional.of(beaconBlock)));
.thenReturn(SafeFuture.completedFuture(Optional.of(blindedBeaconBlock)));
when(executionLayerBlockProductionManager.getCachedPayloadResult(UInt64.ONE))
.thenReturn(
Optional.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ public static <T> T parse(final String json, final DeserializableTypeDefinition<
return parse(() -> FACTORY.createParser(json), type);
}

public static <T> T parse(
final String json, final DeserializableOneOfTypeDefinition<T, ?> oneOfType)
public static <T> T parse(final String json, final DeserializableOneOfTypeDefinition<T> oneOfType)
throws JsonProcessingException {
final DeserializableTypeDefinition<? extends T> typeDefinition =
oneOfType.getMatchingType(json);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import java.util.Optional;
import java.util.function.Predicate;

public class DeserializableOneOfTypeDefinition<TObject, TBuilder>
public class DeserializableOneOfTypeDefinition<TObject>
extends SerializableOneOfTypeDefinition<TObject> {

private final Map<Predicate<String>, DeserializableTypeDefinition<? extends TObject>> parserTypes;
Expand All @@ -36,8 +36,7 @@ public class DeserializableOneOfTypeDefinition<TObject, TBuilder>

public static <TObject, TBuilder>
DeserializableOneOfTypeDefinitionBuilder<TObject, TBuilder> object(
@SuppressWarnings("unused") final Class<TObject> type,
@SuppressWarnings("unused") final Class<TBuilder> builderType) {
@SuppressWarnings("unused") final Class<TObject> type) {
return object();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public <T extends TObject> DeserializableOneOfTypeDefinitionBuilder<TObject, TBu
return this;
}

public DeserializableOneOfTypeDefinition<TObject, TBuilder> build() {
public DeserializableOneOfTypeDefinition<TObject> build() {
return new DeserializableOneOfTypeDefinition<>(
name, title.or(() -> name), description, types, parserTypes);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@
import tech.pegasys.teku.infrastructure.json.JsonUtil;

public class DeserializableOneOfTypeDefinitionBuilderTest {
public static final DeserializableOneOfTypeDefinition<
OneOfTypeTestTypeDefinition.TestType, TestTypeBuilder>
public static final DeserializableOneOfTypeDefinition<OneOfTypeTestTypeDefinition.TestType>
DESERIALIZABLE_ONE_OF_TYPE_DEFINITION =
DeserializableOneOfTypeDefinition.object(
OneOfTypeTestTypeDefinition.TestType.class, TestTypeBuilder.class)
DeserializableOneOfTypeDefinition.object(OneOfTypeTestTypeDefinition.TestType.class)
.description("meaningful description")
.withType(
OneOfTypeTestTypeDefinition.TestObjA.isInstance,
Expand All @@ -44,30 +42,4 @@ void shouldBuildDeserializableOneOfType() throws JsonProcessingException {
JsonUtil.parse("{\"value1\":\"FOO\"}", DESERIALIZABLE_ONE_OF_TYPE_DEFINITION);
assertThat(result).isInstanceOf(OneOfTypeTestTypeDefinition.TestObjA.class);
}

@SuppressWarnings("unused")
private static class TestTypeBuilder {
private String value1;
private String value2;

public TestTypeBuilder value1(final String value1) {
this.value1 = value1;
return this;
}

public TestTypeBuilder value2(final String value2) {
this.value2 = value2;
return this;
}

public OneOfTypeTestTypeDefinition.TestType build() {
if (value1 != null) {
return new OneOfTypeTestTypeDefinition.TestObjA(value1);
}
if (value2 != null) {
return new OneOfTypeTestTypeDefinition.TestObjB(value2);
}
throw new IllegalArgumentException("No class matches");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
/*
* 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.handlers;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assumptions.assumeThat;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_EXECUTION_PAYLOAD_BLINDED;
import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ONE;
import static tech.pegasys.teku.spec.SpecMilestone.BELLATRIX;
import static tech.pegasys.teku.spec.SpecMilestone.CAPELLA;
import static tech.pegasys.teku.spec.SpecMilestone.DENEB;

import com.google.common.net.MediaType;
import java.util.Optional;
import okhttp3.mockwebserver.MockResponse;
import okio.Buffer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestTemplate;
import tech.pegasys.teku.bls.BLSSignature;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.spec.TestSpecContext;
import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock;
import tech.pegasys.teku.spec.datastructures.blocks.BlockContainer;
import tech.pegasys.teku.spec.datastructures.blocks.versions.deneb.BlindedBlockContents;
import tech.pegasys.teku.spec.datastructures.blocks.versions.deneb.BlockContents;
import tech.pegasys.teku.spec.networks.Eth2Network;
import tech.pegasys.teku.validator.remote.typedef.AbstractTypeDefRequestTestBase;

@TestSpecContext(allMilestones = true, network = Eth2Network.MINIMAL)
public class ProduceBlockRequestTest extends AbstractTypeDefRequestTestBase {

private ProduceBlockRequest request;
private Buffer responseBodyBuffer;

@BeforeEach
void setupRequest() {
request =
new ProduceBlockRequest(mockWebServer.url("/"), okHttpClient, spec, UInt64.ONE, false);
responseBodyBuffer = new Buffer();
}

@AfterEach
void reset() {
responseBodyBuffer.clear();
responseBodyBuffer.close();
}

@TestTemplate
public void shouldGetUnblindedBeaconBlockAsJson() {
assumeThat(specMilestone).isLessThan(DENEB);
final BeaconBlock beaconBlock = dataStructureUtil.randomBeaconBlock(ONE);
final ProduceBlockRequest.ProduceBlockResponse blockResponse =
new ProduceBlockRequest.ProduceBlockResponse(beaconBlock);

final String mockResponse = readExpectedJsonResource(specMilestone, false, false);

mockWebServer.enqueue(new MockResponse().setResponseCode(SC_OK).setBody(mockResponse));

final BLSSignature signature = beaconBlock.getBlock().getBody().getRandaoReveal();

final Optional<BlockContainer> maybeBlockContainer =
request.createUnsignedBlock(signature, Optional.empty());

assertThat(maybeBlockContainer).isPresent();

assertThat(maybeBlockContainer.get()).isEqualTo(blockResponse.getData());
}

@TestTemplate
public void shouldGetUnblindedBeaconBlockAsSsz() {
assumeThat(specMilestone).isLessThan(DENEB);
final BeaconBlock beaconBlock = dataStructureUtil.randomBeaconBlock(ONE);
final ProduceBlockRequest.ProduceBlockResponse blockResponse =
new ProduceBlockRequest.ProduceBlockResponse(beaconBlock);

responseBodyBuffer.write(
spec.getGenesisSchemaDefinitions()
.getBlockContainerSchema()
.sszSerialize(beaconBlock)
.toArrayUnsafe());

mockWebServer.enqueue(
new MockResponse()
.setResponseCode(SC_OK)
.setHeader("Content-Type", MediaType.OCTET_STREAM)
.setBody(responseBodyBuffer));

final BLSSignature signature = beaconBlock.getBlock().getBody().getRandaoReveal();

final Optional<BlockContainer> maybeBlockContainer =
request.createUnsignedBlock(signature, Optional.empty());

assertThat(maybeBlockContainer).isPresent();

assertThat(maybeBlockContainer.get()).isEqualTo(blockResponse.getData());
}

@TestTemplate
public void shouldGetBlindedBeaconBlockAsJson() {
assumeThat(specMilestone).isGreaterThanOrEqualTo(BELLATRIX).isLessThanOrEqualTo(CAPELLA);
final BeaconBlock blindedBeaconBlock = dataStructureUtil.randomBlindedBeaconBlock(ONE);
final ProduceBlockRequest.ProduceBlockResponse blockResponse =
new ProduceBlockRequest.ProduceBlockResponse(blindedBeaconBlock);

final String mockResponse = readExpectedJsonResource(specMilestone, true, false);

mockWebServer.enqueue(new MockResponse().setResponseCode(SC_OK).setBody(mockResponse));

final BLSSignature signature = blindedBeaconBlock.getBlock().getBody().getRandaoReveal();

final Optional<BlockContainer> maybeBlockContainer =
request.createUnsignedBlock(signature, Optional.empty());

assertThat(maybeBlockContainer).isPresent();

assertThat(maybeBlockContainer.get()).isEqualTo(blockResponse.getData());
}

@TestTemplate
public void shouldGetBlindedBeaconBlockAsSsz() {
assumeThat(specMilestone).isLessThan(DENEB);
final BeaconBlock blindedBeaconBlock = dataStructureUtil.randomBlindedBeaconBlock(ONE);
final ProduceBlockRequest.ProduceBlockResponse blockResponse =
new ProduceBlockRequest.ProduceBlockResponse(blindedBeaconBlock);

responseBodyBuffer.write(
spec.getGenesisSchemaDefinitions()
.getBlindedBlockContainerSchema()
.sszSerialize(blindedBeaconBlock)
.toArrayUnsafe());

mockWebServer.enqueue(
new MockResponse()
.setResponseCode(SC_OK)
.setHeader(HEADER_EXECUTION_PAYLOAD_BLINDED, "true")
.setHeader("Content-Type", MediaType.OCTET_STREAM)
.setBody(responseBodyBuffer));

final BLSSignature signature = blindedBeaconBlock.getBlock().getBody().getRandaoReveal();

final Optional<BlockContainer> maybeBlockContainer =
request.createUnsignedBlock(signature, Optional.empty());

assertThat(maybeBlockContainer).isPresent();

assertThat(maybeBlockContainer.get()).isEqualTo(blockResponse.getData());
}

@TestTemplate
public void shouldGetUnblindedBlockContentsPostDenebAsJson() {
assumeThat(specMilestone).isEqualTo(DENEB);
final BlockContents blockContents = dataStructureUtil.randomBlockContents(ONE);
final ProduceBlockRequest.ProduceBlockResponse blockResponse =
new ProduceBlockRequest.ProduceBlockResponse(blockContents);

final String mockResponse = readExpectedJsonResource(specMilestone, false, true);

mockWebServer.enqueue(new MockResponse().setResponseCode(SC_OK).setBody(mockResponse));

final BLSSignature signature = blockContents.getBlock().getBody().getRandaoReveal();

final Optional<BlockContainer> maybeBlockContainer =
request.createUnsignedBlock(signature, Optional.empty());

assertThat(maybeBlockContainer).isPresent();

assertThat(maybeBlockContainer.get()).isEqualTo(blockResponse.getData());
}

@TestTemplate
public void shouldGetUnblindedBlockContentsPostDenebAsSsz() {
assumeThat(specMilestone).isEqualTo(DENEB);
final BlockContents blockContents = dataStructureUtil.randomBlockContents(ONE);
final ProduceBlockRequest.ProduceBlockResponse blockResponse =
new ProduceBlockRequest.ProduceBlockResponse(blockContents);

responseBodyBuffer.write(
spec.getGenesisSchemaDefinitions()
.getBlockContainerSchema()
.sszSerialize(blockContents)
.toArray());

mockWebServer.enqueue(
new MockResponse()
.setResponseCode(SC_OK)
.setHeader("Content-Type", MediaType.OCTET_STREAM)
.setBody(responseBodyBuffer));

final BLSSignature signature = blockContents.getBlock().getBody().getRandaoReveal();

final Optional<BlockContainer> maybeBlockContainer =
request.createUnsignedBlock(signature, Optional.empty());

assertThat(maybeBlockContainer).isPresent();

assertThat(maybeBlockContainer.get()).isEqualTo(blockResponse.getData());
}

@TestTemplate
public void shouldGetBlindedBlockContentsPostDenebAsJson() {
assumeThat(specMilestone).isEqualTo(DENEB);
final BlindedBlockContents blindedBlockContents =
dataStructureUtil.randomBlindedBlockContents(ONE);
final ProduceBlockRequest.ProduceBlockResponse blockResponse =
new ProduceBlockRequest.ProduceBlockResponse(blindedBlockContents);

final String mockResponse = readExpectedJsonResource(specMilestone, true, true);

mockWebServer.enqueue(new MockResponse().setResponseCode(SC_OK).setBody(mockResponse));

final BLSSignature signature = blindedBlockContents.getBlock().getBody().getRandaoReveal();

final Optional<BlockContainer> maybeBlockContainer =
request.createUnsignedBlock(signature, Optional.empty());

assertThat(maybeBlockContainer).isPresent();

assertThat(maybeBlockContainer.get()).isEqualTo(blockResponse.getData());
}

@TestTemplate
public void shouldGetBlindedBlockContentsPostDenebAsSsz() {
assumeThat(specMilestone).isEqualTo(DENEB);
final BlindedBlockContents blindedBlockContents =
dataStructureUtil.randomBlindedBlockContents(ONE);
final ProduceBlockRequest.ProduceBlockResponse blockResponse =
new ProduceBlockRequest.ProduceBlockResponse(blindedBlockContents);

responseBodyBuffer.write(
spec.getGenesisSchemaDefinitions()
.getBlindedBlockContainerSchema()
.sszSerialize(blindedBlockContents)
.toArray());

mockWebServer.enqueue(
new MockResponse()
.setResponseCode(SC_OK)
.setHeader(HEADER_EXECUTION_PAYLOAD_BLINDED, "true")
.setHeader("Content-Type", MediaType.OCTET_STREAM)
.setBody(responseBodyBuffer));

final BLSSignature signature = blindedBlockContents.getBlock().getBody().getRandaoReveal();

final Optional<BlockContainer> maybeBlockContainer =
request.createUnsignedBlock(signature, Optional.empty());

assertThat(maybeBlockContainer).isPresent();

assertThat(maybeBlockContainer.get()).isEqualTo(blockResponse.getData());
}

private String readExpectedJsonResource(
final SpecMilestone specMilestone, final boolean blinded, final boolean blockContents) {
final String fileName =
String.format(
"new%s%s%s.json",
blinded ? "Blinded" : "",
blockContents ? "BlockContents" : "Block",
specMilestone.name());
return readResource("responses/produce_block_responses/" + fileName);
}
}
Loading

0 comments on commit 30d8007

Please sign in to comment.