diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b93ab9c9da..a894484ffe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Breaking Changes ### Additions and Improvements + - Added beacon-api `/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals` endpoint for use post-electra. - Added beacon-api `/eth/v1/beacon/states/{state_id}/pending_deposits` endpoint for use post-electra. ### Bug Fixes diff --git a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/v1/beacon/GetStatePendingPartialWithdrawalsIntegrationTest.java b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/v1/beacon/GetStatePendingPartialWithdrawalsIntegrationTest.java new file mode 100644 index 00000000000..610d6ff9172 --- /dev/null +++ b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/v1/beacon/GetStatePendingPartialWithdrawalsIntegrationTest.java @@ -0,0 +1,49 @@ +/* + * Copyright Consensys Software Inc., 2025 + * + * 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.v1.beacon; + +import static org.assertj.core.api.Assertions.assertThat; +import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_CONSENSUS_VERSION; + +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import okhttp3.Response; +import org.junit.jupiter.api.Test; +import tech.pegasys.teku.api.schema.Version; +import tech.pegasys.teku.beaconrestapi.AbstractDataBackedRestAPIIntegrationTest; +import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetStatePendingPartialWithdrawals; +import tech.pegasys.teku.infrastructure.json.JsonTestUtil; +import tech.pegasys.teku.spec.SpecMilestone; + +public class GetStatePendingPartialWithdrawalsIntegrationTest + extends AbstractDataBackedRestAPIIntegrationTest { + @Test + public void shouldGetElectraDepositsJson() throws Exception { + startRestAPIAtGenesis(SpecMilestone.ELECTRA); + createBlocksAtSlots(10); + final Response response = get("head"); + + final String responseText = response.body().string(); + final JsonNode node = JsonTestUtil.parseAsJsonNode(responseText); + assertThat(node.get("version").asText()).isEqualTo("electra"); + assertThat(node.get("execution_optimistic").asBoolean()).isFalse(); + assertThat(node.get("finalized").asBoolean()).isFalse(); + assertThat(node.get("data").size()).isEqualTo(0); + assertThat(response.header(HEADER_CONSENSUS_VERSION)).isEqualTo(Version.electra.name()); + } + + public Response get(final String stateId) throws IOException { + return getResponse(GetStatePendingPartialWithdrawals.ROUTE.replace("{state_id}", stateId)); + } +} diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_pending_deposits.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_pending_deposits.json index e647aec1a41..4daacc4478c 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_pending_deposits.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_pending_deposits.json @@ -1,6 +1,6 @@ { "get" : { - "tags" : [ "Beacon", "Experimental" ], + "tags" : [ "Beacon" ], "operationId" : "getPendingDeposits", "summary" : "Get pending deposits from state", "description" : "Returns pending deposits for state with given 'stateId'. Should return 400 if requested before electra.", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_pending_partial_withdrawals.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_pending_partial_withdrawals.json new file mode 100644 index 00000000000..46e5bbe88f9 --- /dev/null +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_pending_partial_withdrawals.json @@ -0,0 +1,101 @@ +{ + "get" : { + "tags" : [ "Beacon" ], + "operationId" : "getPendingPartialWithdrawals", + "summary" : "Get pending partial withdrawals from state", + "description" : "Returns pending partial withdrawals for state with given 'stateId'. Should return 400 if requested before electra.", + "parameters" : [ { + "name" : "state_id", + "required" : true, + "in" : "path", + "schema" : { + "type" : "string", + "description" : "State identifier. Can be one of: \"head\" (canonical head in node's view), \"genesis\", \"finalized\", \"justified\", <slot>, <hex encoded stateRoot with 0x prefix>.", + "example" : "head" + } + } ], + "responses" : { + "200" : { + "description" : "Request successful", + "headers" : { + "Eth-Consensus-Version" : { + "description" : "Required in response so client can deserialize returned json or ssz data more effectively.", + "required" : true, + "schema" : { + "type" : "string", + "enum" : [ "phase0", "altair", "bellatrix", "capella", "deneb", "electra" ], + "example" : "phase0" + } + } + }, + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GetPendingPartialWithdrawalsResponse" + } + }, + "application/octet-stream" : { + "schema" : { + "type" : "string", + "format" : "binary" + } + } + } + }, + "404" : { + "description" : "Not found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/HttpErrorResponse" + } + } + } + }, + "415" : { + "description" : "Unsupported media type", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/HttpErrorResponse" + } + } + } + }, + "503" : { + "description" : "Service unavailable", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/HttpErrorResponse" + } + } + } + }, + "204" : { + "description" : "Data is unavailable because the chain has not yet reached genesis", + "content" : { } + }, + "400" : { + "description" : "The request could not be processed, check the response for more information.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/HttpErrorResponse" + } + } + } + }, + "500" : { + "description" : "Internal server error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/HttpErrorResponse" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/GetPendingPartialWithdrawalsResponse.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/GetPendingPartialWithdrawalsResponse.json new file mode 100644 index 00000000000..cf0f5c2f217 --- /dev/null +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/GetPendingPartialWithdrawalsResponse.json @@ -0,0 +1,23 @@ +{ + "title" : "GetPendingPartialWithdrawalsResponse", + "type" : "object", + "required" : [ "version", "execution_optimistic", "finalized", "data" ], + "properties" : { + "version" : { + "type" : "string", + "enum" : [ "phase0", "altair", "bellatrix", "capella", "deneb", "electra" ] + }, + "execution_optimistic" : { + "type" : "boolean" + }, + "finalized" : { + "type" : "boolean" + }, + "data" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PendingPartialWithdrawal" + } + } + } +} \ No newline at end of file diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/JsonTypeDefinitionBeaconRestApi.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/JsonTypeDefinitionBeaconRestApi.java index 32ccce20d16..ba50aef0054 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/JsonTypeDefinitionBeaconRestApi.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/JsonTypeDefinitionBeaconRestApi.java @@ -55,6 +55,7 @@ import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetStateFinalityCheckpoints; import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetStateFork; import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetStatePendingDeposits; +import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetStatePendingPartialWithdrawals; import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetStateRandao; import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetStateRoot; import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetStateSyncCommittees; @@ -253,6 +254,7 @@ private static RestApi create( .endpoint(new PostSyncCommittees(dataProvider)) .endpoint(new PostValidatorLiveness(dataProvider)) .endpoint(new GetStatePendingDeposits(dataProvider, schemaCache)) + .endpoint(new GetStatePendingPartialWithdrawals(dataProvider, schemaCache)) .endpoint(new GetDepositSnapshot(eth1DataProvider)) // Event Handler .endpoint( diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStatePendingDeposits.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStatePendingDeposits.java index 651290a3ec8..ab4159cdfb4 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStatePendingDeposits.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStatePendingDeposits.java @@ -22,7 +22,6 @@ import static tech.pegasys.teku.infrastructure.http.RestApiConstants.FINALIZED; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_CONSENSUS_VERSION; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_BEACON; -import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_EXPERIMENTAL; import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.BOOLEAN_TYPE; import static tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition.listOf; @@ -63,7 +62,7 @@ public GetStatePendingDeposits( .description( "Returns pending deposits for state with given 'stateId'. Should return 400 if requested before electra.") .pathParam(PARAMETER_STATE_ID) - .tags(TAG_BEACON, TAG_EXPERIMENTAL) + .tags(TAG_BEACON) .response( SC_OK, "Request successful", diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStatePendingPartialWithdrawals.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStatePendingPartialWithdrawals.java new file mode 100644 index 00000000000..1bb69beb651 --- /dev/null +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStatePendingPartialWithdrawals.java @@ -0,0 +1,120 @@ +/* + * Copyright Consensys Software Inc., 2025 + * + * 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.handlers.v1.beacon; + +import static tech.pegasys.teku.beaconrestapi.BeaconRestApiTypes.PARAMETER_STATE_ID; +import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.ETH_CONSENSUS_HEADER_TYPE; +import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.MILESTONE_TYPE; +import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.sszResponseType; +import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK; +import static tech.pegasys.teku.infrastructure.http.RestApiConstants.EXECUTION_OPTIMISTIC; +import static tech.pegasys.teku.infrastructure.http.RestApiConstants.FINALIZED; +import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_CONSENSUS_VERSION; +import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_BEACON; +import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.BOOLEAN_TYPE; +import static tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition.listOf; + +import com.fasterxml.jackson.core.JsonProcessingException; +import java.util.List; +import java.util.Optional; +import tech.pegasys.teku.api.ChainDataProvider; +import tech.pegasys.teku.api.DataProvider; +import tech.pegasys.teku.api.schema.Version; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition; +import tech.pegasys.teku.infrastructure.restapi.endpoints.AsyncApiResponse; +import tech.pegasys.teku.infrastructure.restapi.endpoints.EndpointMetadata; +import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiEndpoint; +import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiRequest; +import tech.pegasys.teku.infrastructure.ssz.SszList; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.datastructures.metadata.ObjectAndMetaData; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionCache; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; + +public class GetStatePendingPartialWithdrawals extends RestApiEndpoint { + public static final String ROUTE = "/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals"; + + private final ChainDataProvider chainDataProvider; + + public GetStatePendingPartialWithdrawals( + final DataProvider dataProvider, final SchemaDefinitionCache schemaDefinitionCache) { + this(dataProvider.getChainDataProvider(), schemaDefinitionCache); + } + + GetStatePendingPartialWithdrawals( + final ChainDataProvider provider, final SchemaDefinitionCache schemaDefinitionCache) { + super( + EndpointMetadata.get(ROUTE) + .operationId("getPendingPartialWithdrawals") + .summary("Get pending partial withdrawals from state") + .description( + "Returns pending partial withdrawals for state with given 'stateId'. Should return 400 if requested before electra.") + .pathParam(PARAMETER_STATE_ID) + .tags(TAG_BEACON) + .response( + SC_OK, + "Request successful", + getResponseType(schemaDefinitionCache), + sszResponseType(), + ETH_CONSENSUS_HEADER_TYPE) + .withNotFoundResponse() + .withUnsupportedMediaTypeResponse() + .withChainDataResponses() + .build()); + this.chainDataProvider = provider; + } + + @Override + public void handleRequest(final RestApiRequest request) throws JsonProcessingException { + + final SafeFuture>>> future = + chainDataProvider.getPendingPartialWithdrawals( + request.getPathParameter(PARAMETER_STATE_ID)); + + request.respondAsync( + future.thenApply( + maybeData -> + maybeData + .map( + objectAndMetadata -> { + request.header( + HEADER_CONSENSUS_VERSION, + Version.fromMilestone(objectAndMetadata.getMilestone()).name()); + return AsyncApiResponse.respondOk(objectAndMetadata); + }) + .orElseGet(AsyncApiResponse::respondNotFound))); + } + + private static SerializableTypeDefinition>> + getResponseType(final SchemaDefinitionCache schemaDefinitionCache) { + final SchemaDefinitionsElectra schemaDefinitionsElectra = + schemaDefinitionCache + .getSchemaDefinition(SpecMilestone.ELECTRA) + .toVersionElectra() + .orElseThrow(); + + final SerializableTypeDefinition pendingPartialWithdrawalType = + schemaDefinitionsElectra.getPendingPartialWithdrawalSchema().getJsonTypeDefinition(); + + return SerializableTypeDefinition.>>object() + .name("GetPendingPartialWithdrawalsResponse") + .withField("version", MILESTONE_TYPE, ObjectAndMetaData::getMilestone) + .withField(EXECUTION_OPTIMISTIC, BOOLEAN_TYPE, ObjectAndMetaData::isExecutionOptimistic) + .withField(FINALIZED, BOOLEAN_TYPE, ObjectAndMetaData::isFinalized) + .withField("data", listOf(pendingPartialWithdrawalType), ObjectAndMetaData::getData) + .build(); + } +} diff --git a/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStatePendingPartialWithdrawalsTest.java b/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStatePendingPartialWithdrawalsTest.java new file mode 100644 index 00000000000..d5dacc50caf --- /dev/null +++ b/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStatePendingPartialWithdrawalsTest.java @@ -0,0 +1,102 @@ +/* + * Copyright Consensys Software Inc., 2025 + * + * 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.handlers.v1.beacon; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_BAD_REQUEST; +import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_INTERNAL_SERVER_ERROR; +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.HttpStatusCodes.SC_UNSUPPORTED_MEDIA_TYPE; +import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.getResponseSszFromMetadata; +import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.getResponseStringFromMetadata; +import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.verifyMetadataErrorResponse; + +import com.fasterxml.jackson.core.JsonProcessingException; +import java.io.IOException; +import java.util.List; +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import tech.pegasys.teku.beaconrestapi.AbstractMigratedBeaconHandlerWithChainDataProviderTest; +import tech.pegasys.teku.infrastructure.ssz.SszList; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.datastructures.metadata.ObjectAndMetaData; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal; + +public class GetStatePendingPartialWithdrawalsTest + extends AbstractMigratedBeaconHandlerWithChainDataProviderTest { + + @BeforeEach + public void setup() { + + final GetStatePendingPartialWithdrawals pendingDepositsHandler = + new GetStatePendingPartialWithdrawals(chainDataProvider, schemaDefinitionCache); + initialise(SpecMilestone.ELECTRA); + genesis(); + setHandler(pendingDepositsHandler); + request.setPathParameter("state_id", "head"); + } + + @Test + void metadata_shouldHandle400() throws JsonProcessingException { + verifyMetadataErrorResponse(handler, SC_BAD_REQUEST); + } + + @Test + void metadata_shouldHandle404() throws JsonProcessingException { + verifyMetadataErrorResponse(handler, SC_NOT_FOUND); + } + + @Test + void metadata_shouldHandle415() throws JsonProcessingException { + verifyMetadataErrorResponse(handler, SC_UNSUPPORTED_MEDIA_TYPE); + } + + @Test + void metadata_shouldHandle500() throws JsonProcessingException { + verifyMetadataErrorResponse(handler, SC_INTERNAL_SERVER_ERROR); + } + + @Test + void metadata_shouldHandle200() throws IOException { + final PendingPartialWithdrawal withdrawal = dataStructureUtil.randomPendingPartialWithdrawal(); + final ObjectAndMetaData> responseData = + new ObjectAndMetaData<>(List.of(withdrawal), SpecMilestone.ELECTRA, false, true, false); + final String data = getResponseStringFromMetadata(handler, SC_OK, responseData); + final String expected = + String.format( + "{\"version\":\"electra\",\"execution_optimistic\":false,\"finalized\":false," + + "\"data\":[{\"validator_index\":\"%s\",\"amount\":\"%s\",\"withdrawable_epoch\":\"%s\"}]}", + withdrawal.getValidatorIndex(), + withdrawal.getAmount(), + withdrawal.getWithdrawableEpoch()); + assertThat(data).isEqualTo(expected); + } + + @Test + void metadata_shouldHandle200OctetStream() throws IOException { + final BeaconStateElectra state = + dataStructureUtil.randomBeaconState().toVersionElectra().orElseThrow(); + final PendingPartialWithdrawal pendingPartialWithdrawal = + dataStructureUtil.randomPendingPartialWithdrawal(); + final SszList deposits = + state.getPendingPartialWithdrawals().getSchema().of(pendingPartialWithdrawal); + final ObjectAndMetaData> responseData = + new ObjectAndMetaData<>(deposits, SpecMilestone.ELECTRA, false, true, false); + final byte[] data = getResponseSszFromMetadata(handler, SC_OK, responseData); + assertThat(Bytes.of(data)).isEqualTo(deposits.sszSerialize()); + } +} diff --git a/data/provider/src/main/java/tech/pegasys/teku/api/ChainDataProvider.java b/data/provider/src/main/java/tech/pegasys/teku/api/ChainDataProvider.java index 33301baf928..695715d8112 100644 --- a/data/provider/src/main/java/tech/pegasys/teku/api/ChainDataProvider.java +++ b/data/provider/src/main/java/tech/pegasys/teku/api/ChainDataProvider.java @@ -73,6 +73,7 @@ import tech.pegasys.teku.spec.datastructures.state.SyncCommittee; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal; import tech.pegasys.teku.spec.logic.common.statetransition.epoch.status.ValidatorStatuses; import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.EpochProcessingException; import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.SlotProcessingException; @@ -759,27 +760,59 @@ public SafeFuture>>> getState private Optional>> getPendingDeposits( final Optional maybeStateAndMetadata) { - if (maybeStateAndMetadata.isPresent()) { - if (!maybeStateAndMetadata - .get() - .getMilestone() - .isGreaterThanOrEqualTo(SpecMilestone.ELECTRA)) { - throw new BadRequestException( - "The state was successfully retrieved, but was prior to electra and does not contain pending deposits."); - } - return maybeStateAndMetadata.map( - stateAndMetaData -> { - final SszList deposits = - stateAndMetaData.getData().toVersionElectra().orElseThrow().getPendingDeposits(); - ; - return new ObjectAndMetaData<>( - deposits, - stateAndMetaData.getMilestone(), - stateAndMetaData.isExecutionOptimistic(), - stateAndMetaData.isCanonical(), - stateAndMetaData.isFinalized()); - }); + + checkMinimumMilestone(maybeStateAndMetadata, SpecMilestone.ELECTRA); + return maybeStateAndMetadata.map( + stateAndMetaData -> { + final SszList deposits = + stateAndMetaData.getData().toVersionElectra().orElseThrow().getPendingDeposits(); + return new ObjectAndMetaData<>( + deposits, + stateAndMetaData.getMilestone(), + stateAndMetaData.isExecutionOptimistic(), + stateAndMetaData.isCanonical(), + stateAndMetaData.isFinalized()); + }); + } + + public SafeFuture>>> + getPendingPartialWithdrawals(final String stateIdParam) { + return stateSelectorFactory + .createSelectorForStateId(stateIdParam) + .getState() + .thenApply(this::getPendingPartialWithdrawals); + } + + private Optional>> + getPendingPartialWithdrawals(final Optional maybeStateAndMetadata) { + checkMinimumMilestone(maybeStateAndMetadata, SpecMilestone.ELECTRA); + + return maybeStateAndMetadata.map( + stateAndMetaData -> { + final SszList withdrawals = + stateAndMetaData + .getData() + .toVersionElectra() + .orElseThrow() + .getPendingPartialWithdrawals(); + return new ObjectAndMetaData<>( + withdrawals, + stateAndMetaData.getMilestone(), + stateAndMetaData.isExecutionOptimistic(), + stateAndMetaData.isCanonical(), + stateAndMetaData.isFinalized()); + }); + } + + private void checkMinimumMilestone( + final Optional maybeStateAndMetadata, + final SpecMilestone minimumMilestone) { + if (maybeStateAndMetadata.isPresent() + && maybeStateAndMetadata.get().getMilestone().isLessThan(minimumMilestone)) { + throw new BadRequestException( + String.format( + "The state was successfully retrieved, but was prior to %s and does not contain pending deposits.", + minimumMilestone.name())); } - return Optional.empty(); } } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/SpecMilestone.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/SpecMilestone.java index 301b589b512..e1956cd7919 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/SpecMilestone.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/SpecMilestone.java @@ -55,6 +55,10 @@ public boolean isLessThanOrEqualTo(final SpecMilestone other) { return compareTo(other) <= 0; } + public boolean isLessThan(final SpecMilestone other) { + return compareTo(other) < 0; + } + /** Returns the milestone prior to this milestone */ @SuppressWarnings("EnumOrdinal") public SpecMilestone getPreviousMilestone() { diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/SpecMilestoneTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/SpecMilestoneTest.java index 004944845ff..5b675c9edc7 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/SpecMilestoneTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/SpecMilestoneTest.java @@ -15,8 +15,19 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; - +import static tech.pegasys.teku.spec.SpecMilestone.ALTAIR; +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 static tech.pegasys.teku.spec.SpecMilestone.ELECTRA; +import static tech.pegasys.teku.spec.SpecMilestone.PHASE0; + +import java.util.stream.Stream; +import org.assertj.core.api.AssertionsForClassTypes; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import tech.pegasys.teku.infrastructure.bytes.Bytes4; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.config.SpecConfig; @@ -57,170 +68,227 @@ public class SpecMilestoneTest { private final SpecConfig phase0SpecConfig = SpecConfigLoader.loadConfig(Eth2Network.MINIMAL.configName()).specConfig(); - @Test - public void isGreaterThanOrEqualTo() { - assertThat(SpecMilestone.PHASE0.isGreaterThanOrEqualTo(SpecMilestone.PHASE0)).isTrue(); - assertThat(SpecMilestone.PHASE0.isGreaterThanOrEqualTo(SpecMilestone.ALTAIR)).isFalse(); - - assertThat(SpecMilestone.ALTAIR.isGreaterThanOrEqualTo(SpecMilestone.PHASE0)).isTrue(); - assertThat(SpecMilestone.ALTAIR.isGreaterThanOrEqualTo(SpecMilestone.ALTAIR)).isTrue(); - assertThat(SpecMilestone.ALTAIR.isGreaterThanOrEqualTo(SpecMilestone.BELLATRIX)).isFalse(); - - assertThat(SpecMilestone.BELLATRIX.isGreaterThanOrEqualTo(SpecMilestone.ALTAIR)).isTrue(); - assertThat(SpecMilestone.BELLATRIX.isGreaterThanOrEqualTo(SpecMilestone.BELLATRIX)).isTrue(); - - assertThat(SpecMilestone.CAPELLA.isGreaterThanOrEqualTo(SpecMilestone.BELLATRIX)).isTrue(); - assertThat(SpecMilestone.CAPELLA.isGreaterThanOrEqualTo(SpecMilestone.CAPELLA)).isTrue(); - assertThat(SpecMilestone.CAPELLA.isGreaterThanOrEqualTo(SpecMilestone.DENEB)).isFalse(); - - assertThat(SpecMilestone.DENEB.isGreaterThanOrEqualTo(SpecMilestone.BELLATRIX)).isTrue(); - assertThat(SpecMilestone.DENEB.isGreaterThanOrEqualTo(SpecMilestone.CAPELLA)).isTrue(); - assertThat(SpecMilestone.DENEB.isGreaterThanOrEqualTo(SpecMilestone.DENEB)).isTrue(); - assertThat(SpecMilestone.DENEB.isGreaterThanOrEqualTo(SpecMilestone.ELECTRA)).isFalse(); - - assertThat(SpecMilestone.ELECTRA.isGreaterThanOrEqualTo(SpecMilestone.CAPELLA)).isTrue(); - assertThat(SpecMilestone.ELECTRA.isGreaterThanOrEqualTo(SpecMilestone.DENEB)).isTrue(); - assertThat(SpecMilestone.ELECTRA.isGreaterThanOrEqualTo(SpecMilestone.ELECTRA)).isTrue(); - } - - @Test - public void getPreviousMilestone() { - assertThrows(IllegalArgumentException.class, SpecMilestone.PHASE0::getPreviousMilestone); - assertThat(SpecMilestone.ALTAIR.getPreviousMilestone()).isEqualTo(SpecMilestone.PHASE0); - assertThat(SpecMilestone.BELLATRIX.getPreviousMilestone()).isEqualTo(SpecMilestone.ALTAIR); - assertThat(SpecMilestone.CAPELLA.getPreviousMilestone()).isEqualTo(SpecMilestone.BELLATRIX); - assertThat(SpecMilestone.DENEB.getPreviousMilestone()).isEqualTo(SpecMilestone.CAPELLA); - assertThat(SpecMilestone.ELECTRA.getPreviousMilestone()).isEqualTo(SpecMilestone.DENEB); + public static Stream isLessThanPermutations() { + return Stream.of( + Arguments.of(PHASE0, PHASE0, false), + Arguments.of(PHASE0, ALTAIR, true), + Arguments.of(PHASE0, BELLATRIX, true), + Arguments.of(PHASE0, CAPELLA, true), + Arguments.of(PHASE0, DENEB, true), + Arguments.of(PHASE0, ELECTRA, true), + Arguments.of(ALTAIR, PHASE0, false), + Arguments.of(ALTAIR, ALTAIR, false), + Arguments.of(ALTAIR, BELLATRIX, true), + Arguments.of(ALTAIR, CAPELLA, true), + Arguments.of(ALTAIR, DENEB, true), + Arguments.of(ALTAIR, ELECTRA, true), + Arguments.of(BELLATRIX, PHASE0, false), + Arguments.of(BELLATRIX, ALTAIR, false), + Arguments.of(BELLATRIX, BELLATRIX, false), + Arguments.of(BELLATRIX, CAPELLA, true), + Arguments.of(BELLATRIX, DENEB, true), + Arguments.of(BELLATRIX, ELECTRA, true), + Arguments.of(CAPELLA, PHASE0, false), + Arguments.of(CAPELLA, ALTAIR, false), + Arguments.of(CAPELLA, BELLATRIX, false), + Arguments.of(CAPELLA, CAPELLA, false), + Arguments.of(CAPELLA, DENEB, true), + Arguments.of(CAPELLA, ELECTRA, true), + Arguments.of(DENEB, PHASE0, false), + Arguments.of(DENEB, ALTAIR, false), + Arguments.of(DENEB, BELLATRIX, false), + Arguments.of(DENEB, CAPELLA, false), + Arguments.of(DENEB, DENEB, false), + Arguments.of(DENEB, ELECTRA, true), + Arguments.of(ELECTRA, PHASE0, false), + Arguments.of(ELECTRA, ALTAIR, false), + Arguments.of(ELECTRA, BELLATRIX, false), + Arguments.of(ELECTRA, CAPELLA, false), + Arguments.of(ELECTRA, DENEB, false), + Arguments.of(ELECTRA, ELECTRA, false)); + } + + public static Stream isGreaterThanOrEqualToPermutations() { + return Stream.of( + Arguments.of(PHASE0, PHASE0, true), + Arguments.of(PHASE0, ALTAIR, false), + Arguments.of(PHASE0, BELLATRIX, false), + Arguments.of(PHASE0, CAPELLA, false), + Arguments.of(PHASE0, DENEB, false), + Arguments.of(PHASE0, ELECTRA, false), + Arguments.of(ALTAIR, PHASE0, true), + Arguments.of(ALTAIR, ALTAIR, true), + Arguments.of(ALTAIR, BELLATRIX, false), + Arguments.of(ALTAIR, CAPELLA, false), + Arguments.of(ALTAIR, DENEB, false), + Arguments.of(ALTAIR, ELECTRA, false), + Arguments.of(BELLATRIX, PHASE0, true), + Arguments.of(BELLATRIX, ALTAIR, true), + Arguments.of(BELLATRIX, BELLATRIX, true), + Arguments.of(BELLATRIX, CAPELLA, false), + Arguments.of(BELLATRIX, DENEB, false), + Arguments.of(BELLATRIX, ELECTRA, false), + Arguments.of(CAPELLA, PHASE0, true), + Arguments.of(CAPELLA, ALTAIR, true), + Arguments.of(CAPELLA, BELLATRIX, true), + Arguments.of(CAPELLA, CAPELLA, true), + Arguments.of(CAPELLA, DENEB, false), + Arguments.of(CAPELLA, ELECTRA, false), + Arguments.of(DENEB, PHASE0, true), + Arguments.of(DENEB, ALTAIR, true), + Arguments.of(DENEB, BELLATRIX, true), + Arguments.of(DENEB, CAPELLA, true), + Arguments.of(DENEB, DENEB, true), + Arguments.of(DENEB, ELECTRA, false), + Arguments.of(ELECTRA, PHASE0, true), + Arguments.of(ELECTRA, ALTAIR, true), + Arguments.of(ELECTRA, BELLATRIX, true), + Arguments.of(ELECTRA, CAPELLA, true), + Arguments.of(ELECTRA, DENEB, true), + Arguments.of(ELECTRA, ELECTRA, true)); + } + + public static Stream getPreviousPermutations() { + return Stream.of( + Arguments.of(ALTAIR, PHASE0), + Arguments.of(BELLATRIX, ALTAIR), + Arguments.of(CAPELLA, BELLATRIX), + Arguments.of(DENEB, CAPELLA), + Arguments.of(ELECTRA, DENEB)); + } + + @ParameterizedTest + @MethodSource("isGreaterThanOrEqualToPermutations") + public void isGreaterThanOrEqualTo( + final SpecMilestone a, final SpecMilestone b, final boolean comparisonResult) { + assertThat(a.isGreaterThanOrEqualTo(b)).isEqualTo(comparisonResult); + } + + @ParameterizedTest + @MethodSource("isLessThanPermutations") + public void isLessThan( + final SpecMilestone a, final SpecMilestone b, final boolean comparisonResult) { + AssertionsForClassTypes.assertThat(a.isLessThan(b)).isEqualTo(comparisonResult); + } + + @ParameterizedTest + @MethodSource("getPreviousPermutations") + public void getPreviousMilestone(final SpecMilestone current, final SpecMilestone previous) { + assertThat(current.getPreviousMilestone()).isEqualTo(previous); + } + + @Test + void getPreviousMilestone_throws() { + assertThrows(IllegalArgumentException.class, PHASE0::getPreviousMilestone); + } + + @Test + void getPreviousMilestoneIfPresent() { + assertThat(PHASE0.getPreviousMilestoneIfExists()).isEmpty(); + assertThat(ALTAIR.getPreviousMilestoneIfExists()).contains(PHASE0); + assertThat(BELLATRIX.getPreviousMilestoneIfExists()).contains(ALTAIR); + assertThat(CAPELLA.getPreviousMilestoneIfExists()).contains(BELLATRIX); } @Test public void getAllPriorMilestones_phase0() { - assertThat(SpecMilestone.getAllPriorMilestones(SpecMilestone.PHASE0)).isEmpty(); + assertThat(SpecMilestone.getAllPriorMilestones(PHASE0)).isEmpty(); } @Test public void getAllPriorMilestones_altair() { - assertThat(SpecMilestone.getAllPriorMilestones(SpecMilestone.ALTAIR)) - .contains(SpecMilestone.PHASE0); + assertThat(SpecMilestone.getAllPriorMilestones(SpecMilestone.ALTAIR)).contains(PHASE0); } @Test public void getAllPriorMilestones_bellatrix() { assertThat(SpecMilestone.getAllPriorMilestones(SpecMilestone.BELLATRIX)) - .contains(SpecMilestone.PHASE0, SpecMilestone.ALTAIR); + .contains(PHASE0, SpecMilestone.ALTAIR); } @Test public void getAllPriorMilestones_capella() { - assertThat(SpecMilestone.getAllPriorMilestones(SpecMilestone.CAPELLA)) - .contains(SpecMilestone.PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX); + assertThat(SpecMilestone.getAllPriorMilestones(CAPELLA)) + .contains(PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX); } @Test public void getAllPriorMilestones_deneb() { - assertThat(SpecMilestone.getAllPriorMilestones(SpecMilestone.DENEB)) - .contains( - SpecMilestone.PHASE0, - SpecMilestone.ALTAIR, - SpecMilestone.BELLATRIX, - SpecMilestone.CAPELLA); + assertThat(SpecMilestone.getAllPriorMilestones(DENEB)) + .contains(PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX, CAPELLA); } @Test public void getAllPriorMilestones_electra() { assertThat(SpecMilestone.getAllPriorMilestones(SpecMilestone.ELECTRA)) - .contains( - SpecMilestone.PHASE0, - SpecMilestone.ALTAIR, - SpecMilestone.BELLATRIX, - SpecMilestone.CAPELLA, - SpecMilestone.DENEB); + .contains(PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX, CAPELLA, DENEB); } @Test public void getMilestonesUpTo_phase0() { - assertThat(SpecMilestone.getMilestonesUpTo(SpecMilestone.PHASE0)) - .contains(SpecMilestone.PHASE0); + assertThat(SpecMilestone.getMilestonesUpTo(PHASE0)).contains(PHASE0); } @Test public void getMilestonesUpTo_altair() { assertThat(SpecMilestone.getMilestonesUpTo(SpecMilestone.ALTAIR)) - .contains(SpecMilestone.PHASE0, SpecMilestone.ALTAIR); + .contains(PHASE0, SpecMilestone.ALTAIR); } @Test public void getMilestonesUpTo_bellatrix() { assertThat(SpecMilestone.getMilestonesUpTo(SpecMilestone.BELLATRIX)) - .contains(SpecMilestone.PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX); + .contains(PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX); } @Test public void getMilestonesUpTo_capella() { - assertThat(SpecMilestone.getMilestonesUpTo(SpecMilestone.CAPELLA)) - .contains(SpecMilestone.PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX); + assertThat(SpecMilestone.getMilestonesUpTo(CAPELLA)) + .contains(PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX); } @Test public void getMilestonesUpTo_deneb() { - assertThat(SpecMilestone.getMilestonesUpTo(SpecMilestone.DENEB)) - .contains( - SpecMilestone.PHASE0, - SpecMilestone.ALTAIR, - SpecMilestone.BELLATRIX, - SpecMilestone.CAPELLA); + assertThat(SpecMilestone.getMilestonesUpTo(DENEB)) + .contains(PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX, CAPELLA); } @Test public void getMilestonesUpTo_electra() { assertThat(SpecMilestone.getMilestonesUpTo(SpecMilestone.ELECTRA)) - .contains( - SpecMilestone.PHASE0, - SpecMilestone.ALTAIR, - SpecMilestone.BELLATRIX, - SpecMilestone.CAPELLA, - SpecMilestone.DENEB); + .contains(PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX, CAPELLA, DENEB); } @Test public void areMilestonesInOrder() { - assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.PHASE0, SpecMilestone.ALTAIR)) - .isTrue(); - assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.PHASE0)).isTrue(); + assertThat(SpecMilestone.areMilestonesInOrder(PHASE0, SpecMilestone.ALTAIR)).isTrue(); + assertThat(SpecMilestone.areMilestonesInOrder(PHASE0)).isTrue(); assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.ALTAIR)).isTrue(); - assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.ALTAIR, SpecMilestone.PHASE0)) - .isFalse(); + assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.ALTAIR, PHASE0)).isFalse(); assertThat( SpecMilestone.areMilestonesInOrder( - SpecMilestone.PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX)) + PHASE0, SpecMilestone.ALTAIR, SpecMilestone.BELLATRIX)) .isTrue(); assertThat( SpecMilestone.areMilestonesInOrder( - SpecMilestone.ALTAIR, SpecMilestone.PHASE0, SpecMilestone.BELLATRIX)) + SpecMilestone.ALTAIR, PHASE0, SpecMilestone.BELLATRIX)) .isFalse(); assertThat( SpecMilestone.areMilestonesInOrder( - SpecMilestone.PHASE0, SpecMilestone.BELLATRIX, SpecMilestone.ALTAIR)) - .isFalse(); - assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.BELLATRIX, SpecMilestone.CAPELLA)) - .isTrue(); - assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.CAPELLA, SpecMilestone.BELLATRIX)) - .isFalse(); - assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.CAPELLA, SpecMilestone.DENEB)) - .isTrue(); - assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.DENEB, SpecMilestone.CAPELLA)) - .isFalse(); - assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.DENEB, SpecMilestone.ELECTRA)) - .isTrue(); - assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.ELECTRA, SpecMilestone.DENEB)) + PHASE0, SpecMilestone.BELLATRIX, SpecMilestone.ALTAIR)) .isFalse(); + assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.BELLATRIX, CAPELLA)).isTrue(); + assertThat(SpecMilestone.areMilestonesInOrder(CAPELLA, SpecMilestone.BELLATRIX)).isFalse(); + assertThat(SpecMilestone.areMilestonesInOrder(CAPELLA, DENEB)).isTrue(); + assertThat(SpecMilestone.areMilestonesInOrder(DENEB, CAPELLA)).isFalse(); + assertThat(SpecMilestone.areMilestonesInOrder(DENEB, SpecMilestone.ELECTRA)).isTrue(); + assertThat(SpecMilestone.areMilestonesInOrder(SpecMilestone.ELECTRA, DENEB)).isFalse(); } @Test public void getForkVersion_phase0() { final Bytes4 expected = altairSpecConfig.getGenesisForkVersion(); - assertThat(SpecMilestone.getForkVersion(altairSpecConfig, SpecMilestone.PHASE0)) - .contains(expected); + assertThat(SpecMilestone.getForkVersion(altairSpecConfig, PHASE0)).contains(expected); } @Test @@ -240,15 +308,13 @@ public void getForkVersion_bellatrix() { @Test public void getForkVersion_capella() { final Bytes4 expected = capellaSpecConfig.getCapellaForkVersion(); - assertThat(SpecMilestone.getForkVersion(capellaSpecConfig, SpecMilestone.CAPELLA)) - .contains(expected); + assertThat(SpecMilestone.getForkVersion(capellaSpecConfig, CAPELLA)).contains(expected); } @Test public void getForkVersion_deneb() { final Bytes4 expected = denebSpecConfig.getDenebForkVersion(); - assertThat(SpecMilestone.getForkVersion(denebSpecConfig, SpecMilestone.DENEB)) - .contains(expected); + assertThat(SpecMilestone.getForkVersion(denebSpecConfig, DENEB)).contains(expected); } @Test @@ -261,8 +327,7 @@ public void getForkVersion_electra() { @Test public void getForkEpoch_phase0() { final UInt64 expected = UInt64.ZERO; - assertThat(SpecMilestone.getForkEpoch(altairSpecConfig, SpecMilestone.PHASE0)) - .contains(expected); + assertThat(SpecMilestone.getForkEpoch(altairSpecConfig, PHASE0)).contains(expected); } @Test @@ -282,14 +347,13 @@ public void getForkEpoch_bellatrix() { @Test public void getForkEpoch_capella() { final UInt64 expected = capellaSpecConfig.getCapellaForkEpoch(); - assertThat(SpecMilestone.getForkEpoch(capellaSpecConfig, SpecMilestone.CAPELLA)) - .contains(expected); + assertThat(SpecMilestone.getForkEpoch(capellaSpecConfig, CAPELLA)).contains(expected); } @Test public void getForkEpoch_deneb() { final UInt64 expected = denebSpecConfig.getDenebForkEpoch(); - assertThat(SpecMilestone.getForkEpoch(denebSpecConfig, SpecMilestone.DENEB)).contains(expected); + assertThat(SpecMilestone.getForkEpoch(denebSpecConfig, DENEB)).contains(expected); } @Test @@ -313,14 +377,12 @@ public void getForkSlot_bellatrixNotScheduled() { @Test public void getForkEpoch_capellaNotScheduled() { - assertThat(SpecMilestone.getForkEpoch(bellatrixSpecConfig, SpecMilestone.CAPELLA)) - .contains(UInt64.MAX_VALUE); + assertThat(SpecMilestone.getForkEpoch(bellatrixSpecConfig, CAPELLA)).contains(UInt64.MAX_VALUE); } @Test public void getForkEpoch_denebNotScheduled() { - assertThat(SpecMilestone.getForkEpoch(capellaSpecConfig, SpecMilestone.DENEB)) - .contains(UInt64.MAX_VALUE); + assertThat(SpecMilestone.getForkEpoch(capellaSpecConfig, DENEB)).contains(UInt64.MAX_VALUE); } @Test diff --git a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java index c64ed60f946..a1c86803364 100644 --- a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java +++ b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java @@ -2642,7 +2642,7 @@ public PendingPartialWithdrawal randomPendingPartialWithdrawal() { return getElectraSchemaDefinitions(randomSlot()) .getPendingPartialWithdrawalSchema() .create( - SszUInt64.of(randomUInt64()), + SszUInt64.of(randomValidatorIndex()), SszUInt64.of(randomUInt64()), SszUInt64.of(randomUInt64())); }