Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented engine_forkchoiceUpdatedV4
Browse files Browse the repository at this point in the history
lucassaldanha committed Oct 18, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 3796cd7 commit d909ad9
Showing 13 changed files with 542 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV1;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV2;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV3;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV4;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadStatusV1;
import tech.pegasys.teku.ethereum.executionclient.schema.Response;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
@@ -76,6 +77,9 @@ SafeFuture<Response<ForkChoiceUpdatedResult>> forkChoiceUpdatedV2(
SafeFuture<Response<ForkChoiceUpdatedResult>> forkChoiceUpdatedV3(
ForkChoiceStateV1 forkChoiceState, Optional<PayloadAttributesV3> payloadAttributes);

SafeFuture<Response<ForkChoiceUpdatedResult>> forkChoiceUpdatedV4(
ForkChoiceStateV1 forkChoiceState, Optional<PayloadAttributesV4> payloadAttributes);

SafeFuture<Response<List<String>>> exchangeCapabilities(List<String> capabilities);

SafeFuture<Response<List<ClientVersionV1>>> getClientVersionV1(ClientVersionV1 clientVersion);
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV1;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV2;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV3;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV4;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadStatusV1;
import tech.pegasys.teku.ethereum.executionclient.schema.Response;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
@@ -144,6 +145,14 @@ public SafeFuture<Response<ForkChoiceUpdatedResult>> forkChoiceUpdatedV3(
() -> delegate.forkChoiceUpdatedV3(forkChoiceState, payloadAttributes));
}

@Override
public SafeFuture<Response<ForkChoiceUpdatedResult>> forkChoiceUpdatedV4(
final ForkChoiceStateV1 forkChoiceState,
final Optional<PayloadAttributesV4> payloadAttributes) {
return taskQueue.queueTask(
() -> delegate.forkChoiceUpdatedV4(forkChoiceState, payloadAttributes));
}

@Override
public SafeFuture<Response<List<String>>> exchangeCapabilities(final List<String> capabilities) {
return taskQueue.queueTask(() -> delegate.exchangeCapabilities(capabilities));
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.ethereum.executionclient.methods;

import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import tech.pegasys.teku.ethereum.executionclient.ExecutionEngineClient;
import tech.pegasys.teku.ethereum.executionclient.response.ResponseUnwrapper;
import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceStateV1;
import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceUpdatedResult;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV4;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.spec.executionlayer.ForkChoiceState;
import tech.pegasys.teku.spec.executionlayer.PayloadBuildingAttributes;

public class EngineForkChoiceUpdatedV4
extends AbstractEngineJsonRpcMethod<
tech.pegasys.teku.spec.executionlayer.ForkChoiceUpdatedResult> {

private static final Logger LOG = LogManager.getLogger();

public EngineForkChoiceUpdatedV4(final ExecutionEngineClient executionEngineClient) {
super(executionEngineClient);
}

@Override
public String getName() {
return EngineApiMethod.ENGINE_FORK_CHOICE_UPDATED.getName();
}

@Override
public int getVersion() {
return 4;
}

@Override
public SafeFuture<tech.pegasys.teku.spec.executionlayer.ForkChoiceUpdatedResult> execute(
final JsonRpcRequestParams params) {
final ForkChoiceState forkChoiceState = params.getRequiredParameter(0, ForkChoiceState.class);
final Optional<PayloadBuildingAttributes> payloadBuildingAttributes =
params.getOptionalParameter(1, PayloadBuildingAttributes.class);

LOG.trace(
"Calling {}(forkChoiceState={}, payloadAttributes={})",
getVersionedName(),
forkChoiceState,
payloadBuildingAttributes);

final Optional<PayloadAttributesV4> maybePayloadAttributes =
payloadBuildingAttributes.flatMap(
attributes ->
PayloadAttributesV4.fromInternalPayloadBuildingAttributesV4(
payloadBuildingAttributes));

return executionEngineClient
.forkChoiceUpdatedV4(
ForkChoiceStateV1.fromInternalForkChoiceState(forkChoiceState), maybePayloadAttributes)
.thenApply(ResponseUnwrapper::unwrapExecutionClientResponseOrThrow)
.thenApply(ForkChoiceUpdatedResult::asInternalExecutionPayload)
.thenPeek(
forkChoiceUpdatedResult ->
LOG.trace(
"Response {}(forkChoiceState={}, payloadAttributes={}) -> {}",
getVersionedName(),
forkChoiceState,
payloadBuildingAttributes,
forkChoiceUpdatedResult));
}
}
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV1;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV2;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV3;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV4;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadStatusV1;
import tech.pegasys.teku.ethereum.executionclient.schema.Response;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
@@ -59,8 +60,11 @@ public class MetricRecordingExecutionEngineClient extends MetricRecordingAbstrac
public static final String FORKCHOICE_UPDATED_WITH_ATTRIBUTES_V2_METHOD =
"forkchoice_updated_with_attributesV2";
public static final String FORKCHOICE_UPDATED_V3_METHOD = "forkchoice_updatedV3";
public static final String FORKCHOICE_UPDATED_V4_METHOD = "forkchoice_updatedV4";
public static final String FORKCHOICE_UPDATED_WITH_ATTRIBUTES_V3_METHOD =
"forkchoice_updated_with_attributesV3";
public static final String FORKCHOICE_UPDATED_WITH_ATTRIBUTES_V4_METHOD =
"forkchoice_updated_with_attributesV4";
public static final String GET_PAYLOAD_V3_METHOD = "get_payloadV3";
public static final String GET_PAYLOAD_V4_METHOD = "get_payloadV4";
public static final String NEW_PAYLOAD_V3_METHOD = "new_payloadV3";
@@ -185,6 +189,17 @@ public SafeFuture<Response<ForkChoiceUpdatedResult>> forkChoiceUpdatedV3(
: FORKCHOICE_UPDATED_V3_METHOD);
}

@Override
public SafeFuture<Response<ForkChoiceUpdatedResult>> forkChoiceUpdatedV4(
final ForkChoiceStateV1 forkChoiceState,
final Optional<PayloadAttributesV4> payloadAttributes) {
return countRequest(
() -> delegate.forkChoiceUpdatedV4(forkChoiceState, payloadAttributes),
payloadAttributes.isPresent()
? FORKCHOICE_UPDATED_WITH_ATTRIBUTES_V4_METHOD
: FORKCHOICE_UPDATED_V4_METHOD);
}

@Override
public SafeFuture<Response<List<String>>> exchangeCapabilities(final List<String> capabilities) {
return countRequest(
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright Consensys Software Inc., 2022
*
* 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.ethereum.executionclient.schema;

import static com.google.common.base.Preconditions.checkNotNull;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.MoreObjects;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.ethereum.executionclient.serialization.UInt64AsHexDeserializer;
import tech.pegasys.teku.ethereum.executionclient.serialization.UInt64AsHexSerializer;
import tech.pegasys.teku.infrastructure.bytes.Bytes20;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.executionlayer.PayloadBuildingAttributes;

public class PayloadAttributesV4 extends PayloadAttributesV3 {

@JsonSerialize(using = UInt64AsHexSerializer.class)
@JsonDeserialize(using = UInt64AsHexDeserializer.class)
public final UInt64 targetBlockCount;

@JsonSerialize(using = UInt64AsHexSerializer.class)
@JsonDeserialize(using = UInt64AsHexDeserializer.class)
public final UInt64 maximumBlobCount;

public PayloadAttributesV4(
final @JsonProperty("timestamp") UInt64 timestamp,
final @JsonProperty("prevRandao") Bytes32 prevRandao,
final @JsonProperty("suggestedFeeRecipient") Bytes20 suggestedFeeRecipient,
final @JsonProperty("withdrawals") List<WithdrawalV1> withdrawals,
final @JsonProperty("parentBeaconBlockRoot") Bytes32 parentBeaconBlockRoot,
final @JsonProperty("targetBlobCount") UInt64 targetBlockCount,
final @JsonProperty("maximumBlobCount") UInt64 maximumBlobCount) {
super(timestamp, prevRandao, suggestedFeeRecipient, withdrawals, parentBeaconBlockRoot);

checkNotNull(targetBlockCount, "targetBlockCount");
checkNotNull(maximumBlobCount, "maximumBlobCount");
this.targetBlockCount = targetBlockCount;
this.maximumBlobCount = maximumBlobCount;
}

public static Optional<PayloadAttributesV4> fromInternalPayloadBuildingAttributesV4(
final Optional<PayloadBuildingAttributes> payloadBuildingAttributes) {
return payloadBuildingAttributes.map(
payloadAttributes ->
new PayloadAttributesV4(
payloadAttributes.getTimestamp(),
payloadAttributes.getPrevRandao(),
payloadAttributes.getFeeRecipient(),
getWithdrawals(payloadAttributes),
payloadAttributes.getParentBeaconBlockRoot(),
payloadAttributes
.getTargetBlobCount()
.orElseThrow(
() ->
new IllegalArgumentException(
"targetBlobCount is required for PayloadAttributesV4")),
payloadAttributes
.getMaximumBlobCount()
.orElseThrow(
() ->
new IllegalArgumentException(
"maximumBlobCount is required for PayloadAttributesV4"))));
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
final PayloadAttributesV4 that = (PayloadAttributesV4) o;
return Objects.equals(targetBlockCount, that.targetBlockCount)
&& Objects.equals(maximumBlobCount, that.maximumBlobCount);
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), targetBlockCount, maximumBlobCount);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("timestamp", timestamp)
.add("prevRandao", prevRandao)
.add("suggestedFeeRecipient", suggestedFeeRecipient)
.add("withdrawals", withdrawals)
.add("parentBeaconBlockRoot", parentBeaconBlockRoot)
.add("targetBlockCount", targetBlockCount)
.add("maximumBlobCount", maximumBlobCount)
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV1;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV2;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV3;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV4;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadStatusV1;
import tech.pegasys.teku.ethereum.executionclient.schema.Response;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
@@ -238,6 +239,19 @@ public SafeFuture<Response<ForkChoiceUpdatedResult>> forkChoiceUpdatedV3(
return web3JClient.doRequest(web3jRequest, EL_ENGINE_BLOCK_EXECUTION_TIMEOUT);
}

@Override
public SafeFuture<Response<ForkChoiceUpdatedResult>> forkChoiceUpdatedV4(
final ForkChoiceStateV1 forkChoiceState,
final Optional<PayloadAttributesV4> payloadAttributes) {
final Request<?, ForkChoiceUpdatedResultWeb3jResponse> web3jRequest =
new Request<>(
"engine_forkchoiceUpdatedV4",
list(forkChoiceState, payloadAttributes.orElse(null)),
web3JClient.getWeb3jService(),
ForkChoiceUpdatedResultWeb3jResponse.class);
return web3JClient.doRequest(web3jRequest, EL_ENGINE_BLOCK_EXECUTION_TIMEOUT);
}

@Override
public SafeFuture<Response<List<String>>> exchangeCapabilities(final List<String> capabilities) {
final Request<?, ExchangeCapabilitiesWeb3jResponse> web3jRequest =
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* 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.ethereum.executionclient.methods;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
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.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.ethereum.executionclient.ExecutionEngineClient;
import tech.pegasys.teku.ethereum.executionclient.response.InvalidRemoteResponseException;
import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceStateV1;
import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceUpdatedResult;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV4;
import tech.pegasys.teku.ethereum.executionclient.schema.PayloadStatusV1;
import tech.pegasys.teku.ethereum.executionclient.schema.Response;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.TestSpecFactory;
import tech.pegasys.teku.spec.executionlayer.ExecutionPayloadStatus;
import tech.pegasys.teku.spec.executionlayer.ForkChoiceState;
import tech.pegasys.teku.spec.executionlayer.PayloadBuildingAttributes;
import tech.pegasys.teku.spec.util.DataStructureUtil;

class EngineForkChoiceUpdatedV4Test {

private final Spec spec = TestSpecFactory.createMinimalDeneb();
private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec);
private final ExecutionEngineClient executionEngineClient = mock(ExecutionEngineClient.class);
private EngineForkChoiceUpdatedV4 jsonRpcMethod;

@BeforeEach
public void setUp() {
jsonRpcMethod = new EngineForkChoiceUpdatedV4(executionEngineClient);
}

@Test
public void shouldReturnExpectedNameAndVersion() {
assertThat(jsonRpcMethod.getName()).isEqualTo("engine_forkchoiceUpdated");
assertThat(jsonRpcMethod.getVersion()).isEqualTo(4);
assertThat(jsonRpcMethod.getVersionedName()).isEqualTo("engine_forkchoiceUpdatedV4");
}

@Test
public void forkChoiceStateParamIsRequired() {
final JsonRpcRequestParams params = new JsonRpcRequestParams.Builder().build();

assertThatThrownBy(() -> jsonRpcMethod.execute(params))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Missing required parameter at index 0");

verifyNoInteractions(executionEngineClient);
}

@Test
public void payloadBuildingAttributesParamIsOptional() {
final ForkChoiceState forkChoiceState = dataStructureUtil.randomForkChoiceState(false);

when(executionEngineClient.forkChoiceUpdatedV4(any(), eq(Optional.empty())))
.thenReturn(dummySuccessfulResponse());

final JsonRpcRequestParams params =
new JsonRpcRequestParams.Builder().add(forkChoiceState).build();

assertThat(jsonRpcMethod.execute(params)).isCompleted();

verify(executionEngineClient).forkChoiceUpdatedV4(any(), eq(Optional.empty()));
}

@Test
public void shouldReturnFailedFutureWithMessageWhenEngineClientRequestFails() {
final ForkChoiceState forkChoiceState = dataStructureUtil.randomForkChoiceState(false);
final String errorResponseFromClient = "error!";

when(executionEngineClient.forkChoiceUpdatedV4(any(), any()))
.thenReturn(dummyFailedResponse(errorResponseFromClient));

final JsonRpcRequestParams params =
new JsonRpcRequestParams.Builder().add(forkChoiceState).build();

assertThat(jsonRpcMethod.execute(params))
.failsWithin(1, TimeUnit.SECONDS)
.withThrowableOfType(ExecutionException.class)
.withRootCauseInstanceOf(InvalidRemoteResponseException.class)
.withMessageContaining(
"Invalid remote response from the execution client: %s", errorResponseFromClient);
}

@Test
public void shouldCallForkChoiceUpdateV4WithPayloadAttributesV4WhenInElectra() {
final ForkChoiceState forkChoiceState = dataStructureUtil.randomForkChoiceState(false);
final PayloadBuildingAttributes payloadBuildingAttributes =
dataStructureUtil.randomPayloadBuildingAttributes(false);
final ForkChoiceStateV1 forkChoiceStateV1 =
ForkChoiceStateV1.fromInternalForkChoiceState(forkChoiceState);
final Optional<PayloadAttributesV4> payloadAttributesV4 =
PayloadAttributesV4.fromInternalPayloadBuildingAttributesV4(
Optional.of(payloadBuildingAttributes));

jsonRpcMethod = new EngineForkChoiceUpdatedV4(executionEngineClient);

when(executionEngineClient.forkChoiceUpdatedV4(forkChoiceStateV1, payloadAttributesV4))
.thenReturn(dummySuccessfulResponse());

final JsonRpcRequestParams params =
new JsonRpcRequestParams.Builder()
.add(forkChoiceState)
.add(payloadBuildingAttributes)
.build();

assertThat(jsonRpcMethod.execute(params)).isCompleted();

verify(executionEngineClient).forkChoiceUpdatedV4(forkChoiceStateV1, payloadAttributesV4);
}

private SafeFuture<Response<ForkChoiceUpdatedResult>> dummySuccessfulResponse() {
return SafeFuture.completedFuture(
new Response<>(
new ForkChoiceUpdatedResult(
new PayloadStatusV1(
ExecutionPayloadStatus.ACCEPTED, dataStructureUtil.randomBytes32(), ""),
dataStructureUtil.randomBytes8())));
}

private SafeFuture<Response<ForkChoiceUpdatedResult>> dummyFailedResponse(
final String errorMessage) {
return SafeFuture.completedFuture(Response.withErrorMessage(errorMessage));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* 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.ethereum.executionclient.schema;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

import java.util.Optional;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.TestSpecFactory;
import tech.pegasys.teku.spec.executionlayer.PayloadBuildingAttributes;
import tech.pegasys.teku.spec.util.DataStructureUtil;

class PayloadAttributesV4Test {

private final Spec spec = TestSpecFactory.createMinimalElectra();
private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec);

@Test
public void buildFromInternalPayload_RequiresTargetBlobCount() {
final PayloadBuildingAttributes pbaMissingTargetBlobCount =
new PayloadBuildingAttributes(
dataStructureUtil.randomUInt64(),
dataStructureUtil.randomUInt64(),
dataStructureUtil.randomUInt64(),
dataStructureUtil.randomBytes32(),
dataStructureUtil.randomEth1Address(),
Optional.empty(),
Optional.empty(),
dataStructureUtil.randomBytes32(),
Optional.empty(),
Optional.of(dataStructureUtil.randomUInt64()));

assertThrows(
IllegalArgumentException.class,
() ->
PayloadAttributesV4.fromInternalPayloadBuildingAttributesV4(
Optional.of(pbaMissingTargetBlobCount)));
}

@Test
public void buildFromInternalPayload_RequiresMaximumBlobCount() {
final PayloadBuildingAttributes pbaMissingMaximumBlobCount =
new PayloadBuildingAttributes(
dataStructureUtil.randomUInt64(),
dataStructureUtil.randomUInt64(),
dataStructureUtil.randomUInt64(),
dataStructureUtil.randomBytes32(),
dataStructureUtil.randomEth1Address(),
Optional.empty(),
Optional.empty(),
dataStructureUtil.randomBytes32(),
Optional.of(dataStructureUtil.randomUInt64()),
Optional.empty());

assertThrows(
IllegalArgumentException.class,
() ->
PayloadAttributesV4.fromInternalPayloadBuildingAttributesV4(
Optional.of(pbaMissingMaximumBlobCount)));
}

@Test
public void buildFromInternalPayload_HasCorrectValues() {
final PayloadBuildingAttributes payloadBuildingAttributes =
dataStructureUtil.randomPayloadBuildingAttributes(false);

final PayloadAttributesV4 payloadAttributesV4 =
PayloadAttributesV4.fromInternalPayloadBuildingAttributesV4(
Optional.of(payloadBuildingAttributes))
.orElseThrow();

assertThat(payloadBuildingAttributes.getTimestamp()).isEqualTo(payloadAttributesV4.timestamp);
assertThat(payloadBuildingAttributes.getPrevRandao()).isEqualTo(payloadAttributesV4.prevRandao);
assertThat(payloadBuildingAttributes.getFeeRecipient())
.isEqualTo(payloadAttributesV4.suggestedFeeRecipient);
assertThat(payloadBuildingAttributes.getWithdrawals())
.hasValueSatisfying(
withdrawals ->
assertEquals(withdrawals.size(), payloadAttributesV4.withdrawals.size()));
assertThat(payloadBuildingAttributes.getParentBeaconBlockRoot())
.isEqualTo(payloadAttributesV4.parentBeaconBlockRoot);
assertThat(payloadBuildingAttributes.getTargetBlobCount())
.hasValue(payloadAttributesV4.targetBlockCount);
assertThat(payloadBuildingAttributes.getMaximumBlobCount())
.hasValue(payloadAttributesV4.maximumBlobCount);
}
}
Original file line number Diff line number Diff line change
@@ -119,6 +119,8 @@ private Map<EngineApiMethod, EngineJsonRpcMethod<?>> electraSupportedMethods() {

methods.put(ENGINE_NEW_PAYLOAD, new EngineNewPayloadV4(executionEngineClient));
methods.put(ENGINE_GET_PAYLOAD, new EngineGetPayloadV4(executionEngineClient, spec));
// TODO EIP-7742 Replace with EngineForkChoiceUpdatedV4
// (https://github.com/Consensys/teku/issues/8745)
methods.put(ENGINE_FORK_CHOICE_UPDATED, new EngineForkChoiceUpdatedV3(executionEngineClient));
methods.put(ENGINE_GET_BLOBS, new EngineGetBlobsV1(executionEngineClient, spec));

Original file line number Diff line number Diff line change
@@ -128,6 +128,7 @@ void engineNewPayload_shouldCallNewPayloadV4() {

@Test
void engineForkChoiceUpdated_shouldCallEngineForkChoiceUpdatedV3() {
// TODO EIP-7742 should call FcUV4 (https://github.com/Consensys/teku/issues/8745)
final ExecutionClientHandler handler = getHandler();
final ForkChoiceState forkChoiceState = dataStructureUtil.randomForkChoiceState(false);
final ForkChoiceStateV1 forkChoiceStateV1 =
Original file line number Diff line number Diff line change
@@ -34,6 +34,8 @@ public class PayloadBuildingAttributes {
private final Optional<SignedValidatorRegistration> validatorRegistration;
private final Optional<List<Withdrawal>> withdrawals;
private final Bytes32 parentBeaconBlockRoot;
private final Optional<UInt64> targetBlobCount;
private final Optional<UInt64> maximumBlobCount;

public PayloadBuildingAttributes(
final UInt64 proposerIndex,
@@ -44,6 +46,30 @@ public PayloadBuildingAttributes(
final Optional<SignedValidatorRegistration> validatorRegistration,
final Optional<List<Withdrawal>> withdrawals,
final Bytes32 parentBeaconBlockRoot) {
this(
proposerIndex,
proposalSlot,
timestamp,
prevRandao,
feeRecipient,
validatorRegistration,
withdrawals,
parentBeaconBlockRoot,
Optional.empty(),
Optional.empty());
}

public PayloadBuildingAttributes(
final UInt64 proposerIndex,
final UInt64 proposalSlot,
final UInt64 timestamp,
final Bytes32 prevRandao,
final Eth1Address feeRecipient,
final Optional<SignedValidatorRegistration> validatorRegistration,
final Optional<List<Withdrawal>> withdrawals,
final Bytes32 parentBeaconBlockRoot,
final Optional<UInt64> targetBlobCount,
final Optional<UInt64> maximumBlobCount) {
this.proposerIndex = proposerIndex;
this.proposalSlot = proposalSlot;
this.timestamp = timestamp;
@@ -52,6 +78,8 @@ public PayloadBuildingAttributes(
this.validatorRegistration = validatorRegistration;
this.withdrawals = withdrawals;
this.parentBeaconBlockRoot = parentBeaconBlockRoot;
this.targetBlobCount = targetBlobCount;
this.maximumBlobCount = maximumBlobCount;
}

public UInt64 getProposerIndex() {
@@ -78,6 +106,14 @@ public Bytes32 getParentBeaconBlockRoot() {
return parentBeaconBlockRoot;
}

public Optional<UInt64> getTargetBlobCount() {
return targetBlobCount;
}

public Optional<UInt64> getMaximumBlobCount() {
return maximumBlobCount;
}

public Optional<SignedValidatorRegistration> getValidatorRegistration() {
return validatorRegistration;
}
@@ -107,7 +143,9 @@ public boolean equals(final Object o) {
&& Objects.equals(feeRecipient, that.feeRecipient)
&& Objects.equals(validatorRegistration, that.validatorRegistration)
&& Objects.equals(withdrawals, that.withdrawals)
&& Objects.equals(parentBeaconBlockRoot, that.parentBeaconBlockRoot);
&& Objects.equals(parentBeaconBlockRoot, that.parentBeaconBlockRoot)
&& Objects.equals(targetBlobCount, that.targetBlobCount)
&& Objects.equals(maximumBlobCount, that.maximumBlobCount);
}

@Override
@@ -120,7 +158,9 @@ public int hashCode() {
feeRecipient,
validatorRegistration,
withdrawals,
parentBeaconBlockRoot);
parentBeaconBlockRoot,
targetBlobCount,
maximumBlobCount);
}

@Override
@@ -134,6 +174,8 @@ public String toString() {
.add("validatorRegistration", validatorRegistration)
.add("withdrawals", withdrawals)
.add("parentBeaconBlockRoot", parentBeaconBlockRoot)
.add("targetBlobCount", targetBlobCount)
.add("maximumBlobCount", maximumBlobCount)
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -1783,7 +1783,9 @@ public PayloadBuildingAttributes randomPayloadBuildingAttributes(
? Optional.of(randomSignedValidatorRegistration())
: Optional.empty(),
randomWithdrawalList(),
randomBytes32());
randomBytes32(),
Optional.of(randomUInt64()),
Optional.of(randomUInt64()));
}

public ClientVersion randomClientVersion() {
Original file line number Diff line number Diff line change
@@ -238,6 +238,8 @@ private Optional<PayloadBuildingAttributes> calculatePayloadBuildingAttributes(

final Eth1Address feeRecipient = getFeeRecipient(proposerInfo, blockSlot);

// TODO EIP-7742 add targetBlobCount and maximumBlobCount
// (https://github.com/Consensys/teku/issues/8745)
return Optional.of(
new PayloadBuildingAttributes(
proposerIndex,
@@ -247,7 +249,9 @@ private Optional<PayloadBuildingAttributes> calculatePayloadBuildingAttributes(
feeRecipient,
validatorRegistration,
spec.getExpectedWithdrawals(state),
currentHeadBlockRoot));
currentHeadBlockRoot,
Optional.empty(),
Optional.empty()));
}

// this function MUST return a fee recipient.

0 comments on commit d909ad9

Please sign in to comment.