Skip to content

Commit

Permalink
update post attestation interface for electra (#8893)
Browse files Browse the repository at this point in the history
  • Loading branch information
rolfyone authored Dec 9, 2024
1 parent 4ac3018 commit bf649d0
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 81 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright Consensys Software Inc., 2024
*
* 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.beaconrestapi.v2.beacon;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import okhttp3.Response;
import org.junit.jupiter.api.BeforeEach;
import tech.pegasys.teku.beaconrestapi.handlers.v2.beacon.PostAttestationsV2;
import tech.pegasys.teku.infrastructure.json.JsonUtil;
import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.spec.TestSpecFactory;
import tech.pegasys.teku.spec.datastructures.operations.Attestation;
import tech.pegasys.teku.spec.datastructures.operations.SingleAttestation;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra;
import tech.pegasys.teku.spec.util.DataStructureUtil;

public class PostAttestationsV2ElectraIntegrationTest extends PostAttestationsV2IntegrationTest {
protected SerializableTypeDefinition<List<SingleAttestation>> attestationsListTypeDef;

@Override
@BeforeEach
void setup() {
spec = TestSpecFactory.createMinimalElectra();
specMilestone = SpecMilestone.ELECTRA;
startRestAPIAtGenesis(specMilestone);
dataStructureUtil = new DataStructureUtil(spec);
attestationsListTypeDef =
SerializableTypeDefinition.listOf(
SchemaDefinitionsElectra.required(spec.getGenesisSchemaDefinitions())
.getSingleAttestationSchema()
.getJsonTypeDefinition());
}

@Override
protected List<Attestation> getAttestationList(final int listSize) {
final List<Attestation> attestations = new ArrayList<>(listSize);
for (int i = 0; i < listSize; i++) {
attestations.add(dataStructureUtil.randomSingleAttestation());
}
return attestations;
}

@Override
@SuppressWarnings("unchecked")
protected Response postAttestations(final List<?> attestations, final String milestone)
throws IOException {
final SerializableTypeDefinition<List<SingleAttestation>> attestationsListTypeDef =
SerializableTypeDefinition.listOf(
SchemaDefinitionsElectra.required(spec.getGenesisSchemaDefinitions())
.getSingleAttestationSchema()
.getJsonTypeDefinition());
if (milestone == null) {
return post(
PostAttestationsV2.ROUTE,
JsonUtil.serialize((List<SingleAttestation>) attestations, attestationsListTypeDef));
}
return post(
PostAttestationsV2.ROUTE,
JsonUtil.serialize((List<SingleAttestation>) attestations, attestationsListTypeDef),
Collections.emptyMap(),
Optional.of(milestone));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_CONSENSUS_VERSION;

import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import okhttp3.Response;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.beaconrestapi.AbstractDataBackedRestAPIIntegrationTest;
import tech.pegasys.teku.beaconrestapi.handlers.v2.beacon.PostAttestationsV2;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
Expand All @@ -35,75 +36,51 @@
import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.spec.TestSpecContext;
import tech.pegasys.teku.spec.TestSpecInvocationContextProvider;
import tech.pegasys.teku.spec.TestSpecFactory;
import tech.pegasys.teku.spec.datastructures.operations.Attestation;
import tech.pegasys.teku.spec.util.DataStructureUtil;
import tech.pegasys.teku.validator.api.SubmitDataError;

@TestSpecContext(milestone = {SpecMilestone.PHASE0, SpecMilestone.ELECTRA})
public class PostAttestationsV2IntegrationTest extends AbstractDataBackedRestAPIIntegrationTest {

private DataStructureUtil dataStructureUtil;
private SpecMilestone specMilestone;
private SerializableTypeDefinition<List<Attestation>> attestationsListTypeDef;
protected DataStructureUtil dataStructureUtil;
protected SpecMilestone specMilestone;

@BeforeEach
void setup(final TestSpecInvocationContextProvider.SpecContext specContext) {
spec = specContext.getSpec();
specMilestone = specContext.getSpecMilestone();
void setup() {
spec = TestSpecFactory.createMinimalPhase0();
specMilestone = SpecMilestone.PHASE0;
startRestAPIAtGenesis(specMilestone);
dataStructureUtil = specContext.getDataStructureUtil();
attestationsListTypeDef =
SerializableTypeDefinition.listOf(
spec.getGenesisSchemaDefinitions()
.getAttestationSchema()
.castTypeToAttestationSchema()
.getJsonTypeDefinition());
dataStructureUtil = new DataStructureUtil(spec);
}

@TestTemplate
@Test
void shouldPostAttestations_NoErrors() throws Exception {
final List<Attestation> attestations =
List.of(dataStructureUtil.randomAttestation(), dataStructureUtil.randomAttestation());
final List<Attestation> attestations = getAttestationList(2);

when(validatorApiChannel.sendSignedAttestations(attestations))
.thenReturn(SafeFuture.completedFuture(Collections.emptyList()));

final Response response =
post(
PostAttestationsV2.ROUTE,
JsonUtil.serialize(attestations, attestationsListTypeDef),
Collections.emptyMap(),
Optional.of(specMilestone.name().toLowerCase(Locale.ROOT)));
final Response response = postAttestations(attestations, specMilestone.name());

assertThat(response.code()).isEqualTo(SC_OK);
assertThat(response.body().string()).isEmpty();
}

@TestTemplate
@Test
void shouldPartiallyPostAttestations_ReturnsErrors() throws Exception {
final SubmitDataError firstSubmitDataError =
new SubmitDataError(UInt64.ZERO, "Bad attestation");
final SubmitDataError secondSubmitDataError =
new SubmitDataError(UInt64.ONE, "Very bad attestation");

final List<Attestation> attestations =
List.of(
dataStructureUtil.randomAttestation(),
dataStructureUtil.randomAttestation(),
dataStructureUtil.randomAttestation());
final List<Attestation> attestations = getAttestationList(3);

when(validatorApiChannel.sendSignedAttestations(attestations))
.thenReturn(
SafeFuture.completedFuture(List.of(firstSubmitDataError, secondSubmitDataError)));

final Response response =
post(
PostAttestationsV2.ROUTE,
JsonUtil.serialize(attestations, attestationsListTypeDef),
Collections.emptyMap(),
Optional.of(specMilestone.name().toLowerCase(Locale.ROOT)));
final Response response = postAttestations(attestations, specMilestone.name());

assertThat(response.code()).isEqualTo(SC_BAD_REQUEST);
final JsonNode resultAsJsonNode = JsonTestUtil.parseAsJsonNode(response.body().string());
Expand All @@ -121,16 +98,14 @@ void shouldPartiallyPostAttestations_ReturnsErrors() throws Exception {
.isEqualTo(secondSubmitDataError.message());
}

@TestTemplate
@Test
void shouldFailWhenMissingConsensusHeader() throws Exception {
final List<Attestation> attestations =
List.of(dataStructureUtil.randomAttestation(), dataStructureUtil.randomAttestation());
final List<Attestation> attestations = getAttestationList(2);

when(validatorApiChannel.sendSignedAttestations(attestations))
.thenReturn(SafeFuture.completedFuture(Collections.emptyList()));

final Response response =
post(PostAttestationsV2.ROUTE, JsonUtil.serialize(attestations, attestationsListTypeDef));
final Response response = postAttestations(attestations, null);

assertThat(response.code()).isEqualTo(SC_BAD_REQUEST);

Expand All @@ -139,20 +114,14 @@ void shouldFailWhenMissingConsensusHeader() throws Exception {
.isEqualTo("Missing required header value for (%s)", HEADER_CONSENSUS_VERSION);
}

@TestTemplate
@Test
void shouldFailWhenBadConsensusHeaderValue() throws Exception {
final List<Attestation> attestations =
List.of(dataStructureUtil.randomAttestation(), dataStructureUtil.randomAttestation());
final List<Attestation> attestations = getAttestationList(2);

when(validatorApiChannel.sendSignedAttestations(attestations))
.thenReturn(SafeFuture.completedFuture(Collections.emptyList()));
final String badConsensusHeaderValue = "NonExistingMileStone";
final Response response =
post(
PostAttestationsV2.ROUTE,
JsonUtil.serialize(attestations, attestationsListTypeDef),
Collections.emptyMap(),
Optional.of(badConsensusHeaderValue));
final Response response = postAttestations(attestations, badConsensusHeaderValue);

assertThat(response.code()).isEqualTo(SC_BAD_REQUEST);

Expand All @@ -163,4 +132,33 @@ void shouldFailWhenBadConsensusHeaderValue() throws Exception {
"Invalid value for (%s) header: %s",
HEADER_CONSENSUS_VERSION, badConsensusHeaderValue));
}

protected List<Attestation> getAttestationList(final int listSize) {
final List<Attestation> attestations = new ArrayList<>(listSize);
for (int i = 0; i < listSize; i++) {
attestations.add(dataStructureUtil.randomAttestation());
}
return attestations;
}

@SuppressWarnings("unchecked")
protected Response postAttestations(final List<?> attestations, final String milestone)
throws IOException {
final SerializableTypeDefinition<List<Attestation>> attestationsListTypeDef =
SerializableTypeDefinition.listOf(
spec.getGenesisSchemaDefinitions()
.getAttestationSchema()
.castTypeToAttestationSchema()
.getJsonTypeDefinition());
if (milestone == null) {
return post(
PostAttestationsV2.ROUTE,
JsonUtil.serialize((List<Attestation>) attestations, attestationsListTypeDef));
}
return post(
PostAttestationsV2.ROUTE,
JsonUtil.serialize((List<Attestation>) attestations, attestationsListTypeDef),
Collections.emptyMap(),
Optional.of(milestone));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
"oneOf" : [ {
"$ref" : "#/components/schemas/AttestationPhase0"
}, {
"$ref" : "#/components/schemas/AttestationElectra"
"$ref" : "#/components/schemas/SingleAttestation"
} ]
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"title" : "SingleAttestation",
"type" : "object",
"required" : [ "committee_index", "attester_index", "data", "signature" ],
"properties" : {
"committee_index" : {
"type" : "string",
"description" : "unsigned 64 bit integer",
"example" : "1",
"format" : "uint64"
},
"attester_index" : {
"type" : "string",
"description" : "unsigned 64 bit integer",
"example" : "1",
"format" : "uint64"
},
"data" : {
"$ref" : "#/components/schemas/AttestationData"
},
"signature" : {
"type" : "string",
"pattern" : "^0x[a-fA-F0-9]{2,}$",
"description" : "SSZ hexadecimal",
"format" : "bytes"
}
}
}
Loading

0 comments on commit bf649d0

Please sign in to comment.