Skip to content

Commit

Permalink
Updated GetPayloadV4 with executionRequests decoding (#8698)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucassaldanha authored Oct 9, 2024
1 parent 7397737 commit 21e76dd
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,35 @@
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSchema;
import tech.pegasys.teku.spec.datastructures.execution.BlobsBundle;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadContext;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadSchema;
import tech.pegasys.teku.spec.datastructures.execution.GetPayloadResponse;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequests;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequestsDataCodec;
import tech.pegasys.teku.spec.schemas.SchemaDefinitions;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsBellatrix;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra;

public class EngineGetPayloadV4 extends AbstractEngineJsonRpcMethod<GetPayloadResponse> {

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

private final Spec spec;
private final ExecutionRequestsDataCodec executionRequestsDataDecoder;

public EngineGetPayloadV4(final ExecutionEngineClient executionEngineClient, final Spec spec) {
super(executionEngineClient);
this.spec = spec;
this.executionRequestsDataDecoder =
new ExecutionRequestsDataCodec(
SchemaDefinitionsElectra.required(
spec.forMilestone(SpecMilestone.ELECTRA).getSchemaDefinitions())
.getExecutionRequestsSchema());
}

@Override
Expand Down Expand Up @@ -76,11 +86,14 @@ public SafeFuture<GetPayloadResponse> execute(final JsonRpcRequestParams params)
final ExecutionPayload executionPayload =
response.executionPayload.asInternalExecutionPayload(payloadSchema);
final BlobsBundle blobsBundle = getBlobsBundle(response, schemaDefinitions);
final ExecutionRequests executionRequests =
executionRequestsDataDecoder.decode(response.executionRequests);
return new GetPayloadResponse(
executionPayload,
response.blockValue,
blobsBundle,
response.shouldOverrideBuilder);
response.shouldOverrideBuilder,
executionRequests);
})
.thenPeek(
getPayloadResponse ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import tech.pegasys.teku.ethereum.executionclient.serialization.BytesDeserializer;
import tech.pegasys.teku.ethereum.executionclient.serialization.BytesSerializer;
import tech.pegasys.teku.ethereum.executionclient.serialization.UInt256AsHexDeserializer;
import tech.pegasys.teku.ethereum.executionclient.serialization.UInt256AsHexSerializer;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSchema;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadSchema;
import tech.pegasys.teku.spec.datastructures.execution.GetPayloadResponse;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequestsDataCodec;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequestsSchema;

public class GetPayloadV4Response {
public final ExecutionPayloadV3 executionPayload;
Expand All @@ -36,26 +42,36 @@ public class GetPayloadV4Response {

public final boolean shouldOverrideBuilder;

@JsonSerialize(contentUsing = BytesSerializer.class)
@JsonDeserialize(contentUsing = BytesDeserializer.class)
public final List<Bytes> executionRequests;

public GetPayloadV4Response(
final @JsonProperty("executionPayload") ExecutionPayloadV3 executionPayload,
final @JsonProperty("blockValue") UInt256 blockValue,
final @JsonProperty("blobsBundle") BlobsBundleV1 blobsBundle,
final @JsonProperty("shouldOverrideBuilder") boolean shouldOverrideBuilder) {
final @JsonProperty("shouldOverrideBuilder") boolean shouldOverrideBuilder,
final @JsonProperty("executionRequests") List<Bytes> executionRequests) {
checkNotNull(executionPayload, "executionPayload");
checkNotNull(blockValue, "blockValue");
checkNotNull(blobsBundle, "blobsBundle");
checkNotNull(executionRequests, "executionRequests");
this.executionPayload = executionPayload;
this.blockValue = blockValue;
this.blobsBundle = blobsBundle;
this.shouldOverrideBuilder = shouldOverrideBuilder;
this.executionRequests = executionRequests;
}

public GetPayloadResponse asInternalGetPayloadResponse(
final ExecutionPayloadSchema<?> executionPayloadSchema, final BlobSchema blobSchema) {
final ExecutionPayloadSchema<?> executionPayloadSchema,
final BlobSchema blobSchema,
final ExecutionRequestsSchema executionRequestsSchema) {
return new GetPayloadResponse(
executionPayload.asInternalExecutionPayload(executionPayloadSchema),
blockValue,
blobsBundle.asInternalBlobsBundle(blobSchema),
shouldOverrideBuilder);
shouldOverrideBuilder,
new ExecutionRequestsDataCodec(executionRequestsSchema).decode(executionRequests));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -37,19 +39,28 @@
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.spec.TestSpecFactory;
import tech.pegasys.teku.spec.datastructures.execution.BlobsBundle;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadContext;
import tech.pegasys.teku.spec.datastructures.execution.GetPayloadResponse;
import tech.pegasys.teku.spec.datastructures.execution.versions.deneb.ExecutionPayloadDeneb;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequests;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequestsDataCodec;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra;
import tech.pegasys.teku.spec.util.DataStructureUtil;

class EngineGetPayloadV4Test {

private final Spec spec = TestSpecFactory.createMinimalElectra();
private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec);
private final ExecutionEngineClient executionEngineClient = mock(ExecutionEngineClient.class);
private final ExecutionRequestsDataCodec executionRequestsDataCodec =
new ExecutionRequestsDataCodec(
SchemaDefinitionsElectra.required(
spec.forMilestone(SpecMilestone.ELECTRA).getSchemaDefinitions())
.getExecutionRequestsSchema());
private EngineGetPayloadV4 jsonRpcMethod;

@BeforeEach
Expand Down Expand Up @@ -117,18 +128,24 @@ public void shouldCallGetPayloadV4AndParseResponseSuccessfully() {
final UInt256 blockValue = UInt256.MAX_VALUE;
final BlobsBundle blobsBundle = dataStructureUtil.randomBlobsBundle();
final ExecutionPayload executionPayloadElectra = dataStructureUtil.randomExecutionPayload();
final ExecutionRequests executionRequests = dataStructureUtil.randomExecutionRequests();
final List<Bytes> encodedExecutionRequests =
executionRequestsDataCodec.encodeWithoutTypePrefix(executionRequests);
assertThat(executionPayloadElectra).isInstanceOf(ExecutionPayloadDeneb.class);

when(executionEngineClient.getPayloadV4(eq(executionPayloadContext.getPayloadId())))
.thenReturn(dummySuccessfulResponse(executionPayloadElectra, blockValue, blobsBundle));
.thenReturn(
dummySuccessfulResponse(
executionPayloadElectra, blockValue, blobsBundle, encodedExecutionRequests));

final JsonRpcRequestParams params =
new JsonRpcRequestParams.Builder().add(executionPayloadContext).add(UInt64.ZERO).build();

jsonRpcMethod = new EngineGetPayloadV4(executionEngineClient, spec);

final GetPayloadResponse expectedGetPayloadResponse =
new GetPayloadResponse(executionPayloadElectra, blockValue, blobsBundle, false);
new GetPayloadResponse(
executionPayloadElectra, blockValue, blobsBundle, false, executionRequests);
assertThat(jsonRpcMethod.execute(params)).isCompletedWithValue(expectedGetPayloadResponse);

verify(executionEngineClient).getPayloadV4(eq(executionPayloadContext.getPayloadId()));
Expand All @@ -138,14 +155,16 @@ public void shouldCallGetPayloadV4AndParseResponseSuccessfully() {
private SafeFuture<Response<GetPayloadV4Response>> dummySuccessfulResponse(
final ExecutionPayload executionPayload,
final UInt256 blockValue,
final BlobsBundle blobsBundle) {
final BlobsBundle blobsBundle,
final List<Bytes> encodedExecutionRequests) {
return SafeFuture.completedFuture(
new Response<>(
new GetPayloadV4Response(
ExecutionPayloadV3.fromInternalExecutionPayload(executionPayload),
blockValue,
BlobsBundleV1.fromInternalBlobsBundle(blobsBundle),
false)));
false,
encodedExecutionRequests)));
}

private SafeFuture<Response<GetPayloadV4Response>> dummyFailedResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ void engineGetPayload_shouldCallGetPayloadV4() {
dataStructureUtil.randomExecutionPayload()),
UInt256.MAX_VALUE,
BlobsBundleV1.fromInternalBlobsBundle(dataStructureUtil.randomBlobsBundle()),
true);
true,
dataStructureUtil.randomEncodedExecutionRequests());
final SafeFuture<Response<GetPayloadV4Response>> dummyResponse =
SafeFuture.completedFuture(new Response<>(responseData));
when(executionEngineClient.getPayloadV4(context.getPayloadId())).thenReturn(dummyResponse);
Expand All @@ -83,9 +84,12 @@ void engineGetPayload_shouldCallGetPayloadV4() {
final ExecutionPayloadSchema<?> executionPayloadSchema =
schemaDefinitionElectra.getExecutionPayloadSchema();
final BlobSchema blobSchema = schemaDefinitionElectra.getBlobSchema();
assertThat(future)
.isCompletedWithValue(
responseData.asInternalGetPayloadResponse(executionPayloadSchema, blobSchema));
final GetPayloadResponse expectedGetPayloadResponse =
responseData.asInternalGetPayloadResponse(
executionPayloadSchema,
blobSchema,
schemaDefinitionElectra.getExecutionRequestsSchema());
assertThat(future).isCompletedWithValue(expectedGetPayloadResponse);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,22 @@
import java.util.Objects;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequests;

public class GetPayloadResponse {

private final ExecutionPayload executionPayload;
private final UInt256 executionPayloadValue;
private final Optional<BlobsBundle> blobsBundle;
private final boolean shouldOverrideBuilder;
private final Optional<ExecutionRequests> executionRequests;

public GetPayloadResponse(final ExecutionPayload executionPayload) {
this.executionPayload = executionPayload;
this.executionPayloadValue = UInt256.ZERO;
this.blobsBundle = Optional.empty();
this.shouldOverrideBuilder = false;
this.executionRequests = Optional.empty();
}

public GetPayloadResponse(
Expand All @@ -38,6 +41,7 @@ public GetPayloadResponse(
this.executionPayloadValue = executionPayloadValue;
this.blobsBundle = Optional.empty();
this.shouldOverrideBuilder = false;
this.executionRequests = Optional.empty();
}

public GetPayloadResponse(
Expand All @@ -49,6 +53,20 @@ public GetPayloadResponse(
this.executionPayloadValue = executionPayloadValue;
this.blobsBundle = Optional.of(blobsBundle);
this.shouldOverrideBuilder = shouldOverrideBuilder;
this.executionRequests = Optional.empty();
}

public GetPayloadResponse(
final ExecutionPayload executionPayload,
final UInt256 executionPayloadValue,
final BlobsBundle blobsBundle,
final boolean shouldOverrideBuilder,
final ExecutionRequests executionRequests) {
this.executionPayload = executionPayload;
this.executionPayloadValue = executionPayloadValue;
this.blobsBundle = Optional.of(blobsBundle);
this.shouldOverrideBuilder = shouldOverrideBuilder;
this.executionRequests = Optional.of(executionRequests);
}

public ExecutionPayload getExecutionPayload() {
Expand All @@ -67,6 +85,10 @@ public boolean getShouldOverrideBuilder() {
return shouldOverrideBuilder;
}

public Optional<ExecutionRequests> getExecutionRequests() {
return executionRequests;
}

@Override
public boolean equals(final Object o) {
if (this == o) {
Expand All @@ -79,13 +101,18 @@ public boolean equals(final Object o) {
return shouldOverrideBuilder == that.shouldOverrideBuilder
&& Objects.equals(executionPayload, that.executionPayload)
&& Objects.equals(executionPayloadValue, that.executionPayloadValue)
&& Objects.equals(blobsBundle, that.blobsBundle);
&& Objects.equals(blobsBundle, that.blobsBundle)
&& Objects.equals(executionRequests, that.executionRequests);
}

@Override
public int hashCode() {
return Objects.hash(
executionPayload, executionPayloadValue, blobsBundle, shouldOverrideBuilder);
executionPayload,
executionPayloadValue,
blobsBundle,
shouldOverrideBuilder,
executionRequests);
}

@Override
Expand All @@ -95,6 +122,7 @@ public String toString() {
.add("executionPayloadValue", executionPayloadValue)
.add("blobsBundle", blobsBundle)
.add("shouldOverrideBuilder", shouldOverrideBuilder)
.add("executionRequests", executionRequests)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,17 @@ public ExecutionRequests decode(final List<Bytes> executionRequestData) {
return executionRequestsBuilder.build();
}

public Bytes32 hash(final ExecutionRequests executionRequests) {
final Bytes sortedEncodedRequests =
encodeWithTypePrefix(executionRequests).stream()
.map(Hash::sha256)
.map(Bytes.class::cast)
.reduce(Bytes.EMPTY, Bytes::concatenate);
return Hash.sha256(sortedEncodedRequests);
}

@VisibleForTesting
List<Bytes> encodeWithTypePrefix(final ExecutionRequests executionRequests) {
public List<Bytes> encodeWithoutTypePrefix(final ExecutionRequests executionRequests) {
final SszList<DepositRequest> depositRequestsSszList =
executionRequestsSchema
.getDepositRequestsSchema()
Expand All @@ -94,18 +103,17 @@ List<Bytes> encodeWithTypePrefix(final ExecutionRequests executionRequests) {
.createFromElements(executionRequests.getConsolidations());

return List.of(
Bytes.concatenate(DEPOSIT_REQUEST_PREFIX, depositRequestsSszList.sszSerialize()),
Bytes.concatenate(WITHDRAWAL_REQUEST_PREFIX, withdrawalRequestsSszList.sszSerialize()),
Bytes.concatenate(
CONSOLIDATION_REQUEST_PREFIX, consolidationRequestsSszList.sszSerialize()));
depositRequestsSszList.sszSerialize(),
withdrawalRequestsSszList.sszSerialize(),
consolidationRequestsSszList.sszSerialize());
}

public Bytes32 hash(final ExecutionRequests executionRequests) {
final Bytes sortedEncodedRequests =
encodeWithTypePrefix(executionRequests).stream()
.map(Hash::sha256)
.map(Bytes.class::cast)
.reduce(Bytes.EMPTY, Bytes::concatenate);
return Hash.sha256(sortedEncodedRequests);
@VisibleForTesting
List<Bytes> encodeWithTypePrefix(final ExecutionRequests executionRequests) {
final List<Bytes> encodeWithoutTypePrefix = encodeWithoutTypePrefix(executionRequests);
return List.of(
Bytes.concatenate(DEPOSIT_REQUEST_PREFIX, encodeWithoutTypePrefix.get(0)),
Bytes.concatenate(WITHDRAWAL_REQUEST_PREFIX, encodeWithoutTypePrefix.get(1)),
Bytes.concatenate(CONSOLIDATION_REQUEST_PREFIX, encodeWithoutTypePrefix.get(2)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositRequest;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequests;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequestsBuilderElectra;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequestsDataCodec;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequestsSchema;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.WithdrawalRequest;
import tech.pegasys.teku.spec.datastructures.forkchoice.VoteTracker;
import tech.pegasys.teku.spec.datastructures.lightclient.LightClientBootstrap;
Expand Down Expand Up @@ -2504,6 +2506,15 @@ public ExecutionRequests randomExecutionRequests() {
.build();
}

public List<Bytes> randomEncodedExecutionRequests() {
final ExecutionRequestsSchema executionRequestsSchema =
SchemaDefinitionsElectra.required(
spec.forMilestone(SpecMilestone.ELECTRA).getSchemaDefinitions())
.getExecutionRequestsSchema();
return new ExecutionRequestsDataCodec(executionRequestsSchema)
.encodeWithoutTypePrefix(randomExecutionRequests());
}

public WithdrawalRequest randomWithdrawalRequest() {
return getElectraSchemaDefinitions(randomSlot())
.getWithdrawalRequestSchema()
Expand Down

0 comments on commit 21e76dd

Please sign in to comment.