Skip to content

Commit

Permalink
[Merge] implements engine_preparePayload and engine_getPayload (#…
Browse files Browse the repository at this point in the history
…4399)

(#4346)

* implements `engine_preparePayload` and `engine_getPayload`
  • Loading branch information
tbenr committed Sep 22, 2021
1 parent 78ca516 commit ac8fb1c
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public SignedBlockAndState createNewBlock(
.attesterSlashings(blockBodyLists.createAttesterSlashings())
.deposits(deposits)
.voluntaryExits(exits)
.executionPayload(() -> createExecutionPayload(spec, blockSlotState)));
.executionPayload(() -> getExecutionPayload(spec, blockSlotState)));

// Sign block and set block signature
final BeaconBlock block = newBlockAndState.getBlock();
Expand Down Expand Up @@ -128,16 +128,13 @@ private Eth1Data get_eth1_data_stub(BeaconState state, UInt64 current_epoch) {
Hash.sha2_256(Hash.sha2_256(SSZ.encodeUInt64(votingPeriod.longValue()))));
}

private static ExecutionPayload createExecutionPayload(Spec spec, BeaconState genericState) {
private static ExecutionPayload getExecutionPayload(Spec spec, BeaconState genericState) {
final BeaconStateMerge state = BeaconStateMerge.required(genericState);
final Bytes32 executionParentHash = state.getLatest_execution_payload_header().getBlock_hash();
final UInt64 timestamp = spec.computeTimeAtSlot(state, state.getSlot());

return spec.atSlot(state.getSlot())
.getExecutionPayloadUtil()
.orElseThrow()
.getExecutionPayload(
ExecutionEngineChannel.NOOP, executionParentHash, timestamp, UInt64.ZERO);
.getExecutionPayload(ExecutionEngineChannel.NOOP, UInt64.ZERO);
}

public int getProposerIndexForSlot(final BeaconState preState, final UInt64 slot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,21 @@
import tech.pegasys.teku.infrastructure.events.ChannelInterface;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload;
import tech.pegasys.teku.ssz.type.Bytes20;

public interface ExecutionEngineChannel extends ChannelInterface {

ExecutionEngineChannel NOOP =
new ExecutionEngineChannel() {

@Override
public SafeFuture<Void> prepareBlock(
Bytes32 parentHash, UInt64 timestamp, UInt64 payloadId) {
return SafeFuture.completedFuture(null);
public SafeFuture<UInt64> preparePayload(
Bytes32 parentHash, UInt64 timestamp, Bytes32 random, Bytes20 feeRecipient) {
return SafeFuture.completedFuture(UInt64.ZERO);
}

@Override
public SafeFuture<ExecutionPayload> assembleBlock(Bytes32 parentHash, UInt64 timestamp) {
public SafeFuture<ExecutionPayload> getPayload(UInt64 payloadId) {
return SafeFuture.completedFuture(new ExecutionPayload());
}

Expand Down Expand Up @@ -64,16 +65,10 @@ public SafeFuture<Block> getPowChainHead() {
}
};

SafeFuture<Void> prepareBlock(Bytes32 parentHash, UInt64 timestamp, UInt64 payloadId);
SafeFuture<UInt64> preparePayload(
Bytes32 parentHash, UInt64 timestamp, Bytes32 random, Bytes20 feeRecipient);

/**
* Requests execution-engine to produce a block.
*
* @param parentHash the hash of execution block to produce atop of
* @param timestamp the timestamp of the beginning of the slot
* @return a response with execution payload
*/
SafeFuture<ExecutionPayload> assembleBlock(Bytes32 parentHash, UInt64 timestamp);
SafeFuture<ExecutionPayload> getPayload(UInt64 payloadId);

/**
* Requests execution-engine to process a block.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload;
import tech.pegasys.teku.spec.executionengine.ExecutionEngineChannel;
import tech.pegasys.teku.ssz.type.Bytes20;

public class ExecutionPayloadUtil {

Expand All @@ -30,19 +31,21 @@ public boolean verifyExecutionStateTransition(
return executionEngineChannel.newBlock(executionPayload).join();
}

public void prepareExecutionPayload(
public UInt64 prepareExecutionPayload(
ExecutionEngineChannel executionEngineChannel,
Bytes32 parentHash,
UInt64 timestamp,
UInt64 payloadId) {
executionEngineChannel.prepareBlock(parentHash, timestamp, payloadId).join();
Bytes32 random,
Bytes20 feeRecipient) {

return executionEngineChannel
.preparePayload(parentHash, timestamp, random, feeRecipient)
.join();
}

public ExecutionPayload getExecutionPayload(
ExecutionEngineChannel executionEngineChannel,
Bytes32 parentHash,
UInt64 timestamp,
UInt64 payloadId) {
return executionEngineChannel.assembleBlock(parentHash, timestamp).join();
ExecutionEngineChannel executionEngineChannel, UInt64 payloadId) {

return executionEngineChannel.getPayload(payloadId).join();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.services.powchain.execution.client.ExecutionEngineClient;
import tech.pegasys.teku.services.powchain.execution.client.Web3JExecutionEngineClient;
import tech.pegasys.teku.services.powchain.execution.client.schema.AssembleBlockRequest;
import tech.pegasys.teku.services.powchain.execution.client.schema.ExecutionPayload;
import tech.pegasys.teku.services.powchain.execution.client.schema.NewBlockResponse;
import tech.pegasys.teku.services.powchain.execution.client.schema.PreparePayloadRequest;
import tech.pegasys.teku.services.powchain.execution.client.schema.PreparePayloadResponse;
import tech.pegasys.teku.services.powchain.execution.client.schema.Response;
import tech.pegasys.teku.spec.executionengine.ExecutionEngineChannel;
import tech.pegasys.teku.ssz.type.Bytes20;

public class ExecutionEngineChannelImpl implements ExecutionEngineChannel {

Expand Down Expand Up @@ -66,24 +68,34 @@ private static void printConsole(String formatString, Object... args) {
}

@Override
public SafeFuture<Void> prepareBlock(Bytes32 parentHash, UInt64 timestamp, UInt64 payloadId) {
return SafeFuture.completedFuture(null);
public SafeFuture<UInt64> preparePayload(
Bytes32 parentHash, UInt64 timestamp, Bytes32 random, Bytes20 feeRecipient) {
return executionEngineClient
.preparePayload(new PreparePayloadRequest(parentHash, timestamp, random, feeRecipient))
.thenApply(ExecutionEngineChannelImpl::unwrapResponseOrThrow)
.thenApply(PreparePayloadResponse::getPayloadId)
.thenPeek(
getPayloadId ->
printConsole(
"engine_preparePayload(parentHash=%s, timestamp=%s, random=%s, feeRecipient=%s) ~> %s",
LogFormatter.formatHashRoot(parentHash),
timestamp.toString(),
LogFormatter.formatHashRoot(random),
feeRecipient.toHexString(),
getPayloadId));
}

@Override
public SafeFuture<tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload> assembleBlock(
Bytes32 parentHash, UInt64 timestamp) {
AssembleBlockRequest request = new AssembleBlockRequest(parentHash, timestamp);
public SafeFuture<tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload> getPayload(
UInt64 payloadId) {

return executionEngineClient
.consensusAssembleBlock(request)
.getPayload(payloadId)
.thenApply(ExecutionEngineChannelImpl::unwrapResponseOrThrow)
.thenApply(ExecutionPayload::asInternalExecutionPayload)
.thenPeek(
executionPayload ->
printConsole(
"consensus_assembleBlock(parent_hash=%s, timestamp=%s) ~> %s",
LogFormatter.formatHashRoot(parentHash), timestamp, executionPayload));
printConsole("engine_getPayload(payloadId=%s) ~> %s", payloadId, executionPayload));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,19 @@
import org.web3j.protocol.core.methods.response.EthBlock;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.services.powchain.execution.client.schema.AssembleBlockRequest;
import tech.pegasys.teku.services.powchain.execution.client.schema.ExecutionPayload;
import tech.pegasys.teku.services.powchain.execution.client.schema.GenericResponse;
import tech.pegasys.teku.services.powchain.execution.client.schema.NewBlockResponse;
import tech.pegasys.teku.services.powchain.execution.client.schema.PreparePayloadRequest;
import tech.pegasys.teku.services.powchain.execution.client.schema.PreparePayloadResponse;
import tech.pegasys.teku.services.powchain.execution.client.schema.Response;
import tech.pegasys.teku.ssz.type.Bytes20;

public interface ExecutionEngineClient {

SafeFuture<Response<ExecutionPayload>> consensusAssembleBlock(AssembleBlockRequest request);
SafeFuture<Response<PreparePayloadResponse>> preparePayload(PreparePayloadRequest request);

SafeFuture<Response<ExecutionPayload>> getPayload(UInt64 payloadId);

SafeFuture<Response<NewBlockResponse>> consensusNewBlock(ExecutionPayload request);

Expand All @@ -45,15 +48,27 @@ public interface ExecutionEngineClient {
new ExecutionEngineClient() {
private final Bytes ZERO_LOGS_BLOOM = Bytes.wrap(new byte[256]);
private UInt64 number = UInt64.ZERO;
private UInt64 payloadId = UInt64.ZERO;
private Optional<PreparePayloadRequest> lastPreparePayloadRequest = Optional.empty();

@Override
public SafeFuture<Response<PreparePayloadResponse>> preparePayload(
PreparePayloadRequest request) {
lastPreparePayloadRequest = Optional.of(request);
payloadId = payloadId.increment();
return SafeFuture.completedFuture(new Response<>(new PreparePayloadResponse(payloadId)));
}

@Override
public SafeFuture<Response<ExecutionPayload>> consensusAssembleBlock(
AssembleBlockRequest request) {
public SafeFuture<Response<ExecutionPayload>> getPayload(UInt64 payloadId) {
PreparePayloadRequest preparePayloadRequest =
lastPreparePayloadRequest.orElseThrow(
() -> new IllegalStateException("preparePayload was not called."));
number = number.increment();
return SafeFuture.completedFuture(
new Response<>(
new ExecutionPayload(
request.parentHash,
preparePayloadRequest.parentHash,
Bytes20.ZERO,
Bytes32.ZERO,
Bytes32.ZERO,
Expand All @@ -62,7 +77,7 @@ public SafeFuture<Response<ExecutionPayload>> consensusAssembleBlock(
number,
UInt64.ZERO,
UInt64.ZERO,
request.timestamp,
preparePayloadRequest.timestamp,
Bytes32.ZERO,
Bytes32.random(),
Arrays.asList(Bytes.random(128), Bytes.random(256), Bytes.random(512)))));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.protocol.http.HttpService;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.services.powchain.execution.client.schema.AssembleBlockRequest;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.services.powchain.execution.client.schema.ExecutionPayload;
import tech.pegasys.teku.services.powchain.execution.client.schema.GenericResponse;
import tech.pegasys.teku.services.powchain.execution.client.schema.NewBlockResponse;
import tech.pegasys.teku.services.powchain.execution.client.schema.PreparePayloadRequest;
import tech.pegasys.teku.services.powchain.execution.client.schema.PreparePayloadResponse;
import tech.pegasys.teku.services.powchain.execution.client.schema.Response;

public class Web3JExecutionEngineClient implements ExecutionEngineClient {
Expand All @@ -40,14 +42,25 @@ public Web3JExecutionEngineClient(String eth1Endpoint) {
}

@Override
public SafeFuture<Response<ExecutionPayload>> consensusAssembleBlock(
AssembleBlockRequest request) {
Request<?, AssembleBlockWeb3jResponse> web3jRequest =
public SafeFuture<Response<PreparePayloadResponse>> preparePayload(
PreparePayloadRequest request) {
Request<?, PreparePayloadWeb3jResponse> web3jRequest =
new Request<>(
"consensus_assembleBlock",
"engine_preparePayload",
Collections.singletonList(request),
web3jService,
AssembleBlockWeb3jResponse.class);
PreparePayloadWeb3jResponse.class);
return doRequest(web3jRequest);
}

@Override
public SafeFuture<Response<ExecutionPayload>> getPayload(UInt64 payloadId) {
Request<?, GetPayloadWeb3jResponse> web3jRequest =
new Request<>(
"engine_getPayload",
Collections.singletonList(payloadId.toString()),
web3jService,
GetPayloadWeb3jResponse.class);
return doRequest(web3jRequest);
}

Expand Down Expand Up @@ -117,8 +130,10 @@ private <T> SafeFuture<Response<T>> doRequest(
return SafeFuture.of(responseFuture);
}

static class AssembleBlockWeb3jResponse
extends org.web3j.protocol.core.Response<ExecutionPayload> {}
static class GetPayloadWeb3jResponse extends org.web3j.protocol.core.Response<ExecutionPayload> {}

static class PreparePayloadWeb3jResponse
extends org.web3j.protocol.core.Response<PreparePayloadResponse> {}

static class NewBlockWeb3jResponse extends org.web3j.protocol.core.Response<NewBlockResponse> {}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2021 ConsenSys AG.
*
* 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.services.powchain.execution.client.schema;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.Objects;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.services.powchain.execution.client.serializer.BytesSerializer;
import tech.pegasys.teku.services.powchain.execution.client.serializer.UInt64AsHexSerializer;
import tech.pegasys.teku.ssz.type.Bytes20;

public class PreparePayloadRequest {
@JsonSerialize(using = BytesSerializer.class)
public final Bytes32 parentHash;

@JsonSerialize(using = UInt64AsHexSerializer.class)
public final UInt64 timestamp;

@JsonSerialize(using = BytesSerializer.class)
public final Bytes32 random;

@JsonSerialize(using = BytesSerializer.class)
public final Bytes20 feeRecipient;

public PreparePayloadRequest(
Bytes32 parentHash, UInt64 timestamp, Bytes32 random, Bytes20 feeRecipient) {
this.parentHash = parentHash;
this.timestamp = timestamp;
this.random = random;
this.feeRecipient = feeRecipient;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PreparePayloadRequest that = (PreparePayloadRequest) o;
return Objects.equals(parentHash, that.parentHash)
&& Objects.equals(timestamp, that.timestamp)
&& Objects.equals(random, that.random)
&& Objects.equals(feeRecipient, that.feeRecipient);
}

@Override
public int hashCode() {
return Objects.hash(parentHash, timestamp, random, feeRecipient);
}

@Override
public String toString() {
return "PreparePayloadRequest{"
+ "parentHash="
+ parentHash
+ ", timestamp="
+ timestamp
+ ", random="
+ random
+ ", feeRecipient="
+ feeRecipient
+ '}';
}
}
Loading

0 comments on commit ac8fb1c

Please sign in to comment.