From 46f43906eda85266c30c3b9390f8b81597b56279 Mon Sep 17 00:00:00 2001 From: Simon Dudley Date: Tue, 20 Dec 2022 14:50:31 +1000 Subject: [PATCH] Add blockValue to EngineGetPayloadResultV2 Ported code over from https://github.com/hyperledger/besu/pull/4833 Signed-off-by: Simon Dudley --- .../internal/results/BlockResultFactory.java | 3 +- .../results/EngineGetPayloadResultV2.java | 142 ++-------------- .../internal/results/PayloadResultV2.java | 158 ++++++++++++++++++ .../engine/EngineGetPayloadV2Test.java | 127 ++++++++++++++ 4 files changed, 300 insertions(+), 130 deletions(-) create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/PayloadResultV2.java create mode 100644 ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlockResultFactory.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlockResultFactory.java index 712e66d0c53..bc3173d0738 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlockResultFactory.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlockResultFactory.java @@ -112,7 +112,8 @@ public EngineGetPayloadResultV2 enginePayloadTransactionCompleteV2(final Block b w.stream() .map(WithdrawalParameter::fromWithdrawal) .collect(Collectors.toList())); - return new EngineGetPayloadResultV2(block.getHeader(), txs, withdrawals); + return new EngineGetPayloadResultV2( + new PayloadResultV2(block.getHeader(), txs, withdrawals), Quantity.create(0)); } public BlockResult transactionHash(final BlockWithMetadata blockWithMetadata) { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineGetPayloadResultV2.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineGetPayloadResultV2.java index 285d434f1c9..a93ffa70061 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineGetPayloadResultV2.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineGetPayloadResultV2.java @@ -14,145 +14,29 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameter; -import org.hyperledger.besu.ethereum.core.BlockHeader; - -import java.util.List; -import java.util.Optional; - import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import org.apache.tuweni.bytes.Bytes32; @JsonPropertyOrder({ - "parentHash", - "feeRecipient", - "stateRoot", - "receiptsRoot", - "logsBloom", - "prevRandao", - "blockNumber", - "gasLimit", - "gasUsed", - "timestamp", - "extraData", - "baseFeePerGas", - "blockHash", - "transactions", - "withdrawals" + "executionPayload", + "blockValue", }) public class EngineGetPayloadResultV2 { - protected final String blockHash; - private final String parentHash; - private final String feeRecipient; - private final String stateRoot; - private final String receiptsRoot; - private final String logsBloom; - private final String prevRandao; - private final String blockNumber; - private final String gasLimit; - private final String gasUsed; - private final String timestamp; - private final String extraData; - private final String baseFeePerGas; - protected final List transactions; - protected final Optional> withdrawals; - - public EngineGetPayloadResultV2( - final BlockHeader header, - final List transactions, - final Optional> withdrawals) { - this.blockNumber = Quantity.create(header.getNumber()); - this.blockHash = header.getHash().toString(); - this.parentHash = header.getParentHash().toString(); - this.logsBloom = header.getLogsBloom().toString(); - this.stateRoot = header.getStateRoot().toString(); - this.receiptsRoot = header.getReceiptsRoot().toString(); - this.extraData = header.getExtraData().toString(); - this.baseFeePerGas = header.getBaseFee().map(Quantity::create).orElse(null); - this.gasLimit = Quantity.create(header.getGasLimit()); - this.gasUsed = Quantity.create(header.getGasUsed()); - this.timestamp = Quantity.create(header.getTimestamp()); - this.transactions = transactions; - this.feeRecipient = header.getCoinbase().toString(); - this.prevRandao = header.getPrevRandao().map(Bytes32::toHexString).orElse(null); - this.withdrawals = withdrawals; - } - - @JsonGetter(value = "blockNumber") - public String getNumber() { - return blockNumber; - } - - @JsonGetter(value = "blockHash") - public String getHash() { - return blockHash; - } - - @JsonGetter(value = "parentHash") - public String getParentHash() { - return parentHash; - } - - @JsonGetter(value = "logsBloom") - public String getLogsBloom() { - return logsBloom; - } - - @JsonGetter(value = "prevRandao") - public String getPrevRandao() { - return prevRandao; - } - - @JsonGetter(value = "stateRoot") - public String getStateRoot() { - return stateRoot; - } - - @JsonGetter(value = "receiptsRoot") - public String getReceiptRoot() { - return receiptsRoot; - } - - @JsonGetter(value = "extraData") - public String getExtraData() { - return extraData; - } - - @JsonGetter(value = "baseFeePerGas") - public String getBaseFeePerGas() { - return baseFeePerGas; - } - - @JsonGetter(value = "gasLimit") - public String getGasLimit() { - return gasLimit; - } - - @JsonGetter(value = "gasUsed") - public String getGasUsed() { - return gasUsed; - } - - @JsonGetter(value = "timestamp") - public String getTimestamp() { - return timestamp; - } + protected final PayloadResultV2 executionPayload; + private final String blockValue; - @JsonGetter(value = "transactions") - public List getTransactions() { - return transactions; + public EngineGetPayloadResultV2(final PayloadResultV2 executionPayload, final String blockValue) { + this.executionPayload = executionPayload; + this.blockValue = blockValue; } - @JsonGetter(value = "feeRecipient") - @JsonInclude(JsonInclude.Include.NON_NULL) - public String getFeeRecipient() { - return feeRecipient; + @JsonGetter(value = "executionPayload") + public PayloadResultV2 getExecutionPayload() { + return executionPayload; } - @JsonGetter(value = "withdrawals") - public List getWithdrawals() { - return withdrawals.orElse(null); + @JsonGetter(value = "blockValue") + public String getBlockValue() { + return blockValue; } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/PayloadResultV2.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/PayloadResultV2.java new file mode 100644 index 00000000000..9db4c8ad302 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/PayloadResultV2.java @@ -0,0 +1,158 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results; + +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameter; +import org.hyperledger.besu.ethereum.core.BlockHeader; + +import java.util.List; +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import org.apache.tuweni.bytes.Bytes32; + +@JsonPropertyOrder({ + "parentHash", + "feeRecipient", + "stateRoot", + "receiptsRoot", + "logsBloom", + "prevRandao", + "blockNumber", + "gasLimit", + "gasUsed", + "timestamp", + "extraData", + "baseFeePerGas", + "blockHash", + "transactions", + "withdrawals" +}) +public class PayloadResultV2 { + protected final String blockHash; + private final String parentHash; + private final String feeRecipient; + private final String stateRoot; + private final String receiptsRoot; + private final String logsBloom; + private final String prevRandao; + private final String blockNumber; + private final String gasLimit; + private final String gasUsed; + private final String timestamp; + private final String extraData; + private final String baseFeePerGas; + protected final List transactions; + protected final Optional> withdrawals; + + public PayloadResultV2( + final BlockHeader header, + final List transactions, + final Optional> withdrawals) { + this.blockNumber = Quantity.create(header.getNumber()); + this.blockHash = header.getHash().toString(); + this.parentHash = header.getParentHash().toString(); + this.logsBloom = header.getLogsBloom().toString(); + this.stateRoot = header.getStateRoot().toString(); + this.receiptsRoot = header.getReceiptsRoot().toString(); + this.extraData = header.getExtraData().toString(); + this.baseFeePerGas = header.getBaseFee().map(Quantity::create).orElse(null); + this.gasLimit = Quantity.create(header.getGasLimit()); + this.gasUsed = Quantity.create(header.getGasUsed()); + this.timestamp = Quantity.create(header.getTimestamp()); + this.transactions = transactions; + this.feeRecipient = header.getCoinbase().toString(); + this.prevRandao = header.getPrevRandao().map(Bytes32::toHexString).orElse(null); + this.withdrawals = withdrawals; + } + + @JsonGetter(value = "blockNumber") + public String getNumber() { + return blockNumber; + } + + @JsonGetter(value = "blockHash") + public String getHash() { + return blockHash; + } + + @JsonGetter(value = "parentHash") + public String getParentHash() { + return parentHash; + } + + @JsonGetter(value = "logsBloom") + public String getLogsBloom() { + return logsBloom; + } + + @JsonGetter(value = "prevRandao") + public String getPrevRandao() { + return prevRandao; + } + + @JsonGetter(value = "stateRoot") + public String getStateRoot() { + return stateRoot; + } + + @JsonGetter(value = "receiptsRoot") + public String getReceiptRoot() { + return receiptsRoot; + } + + @JsonGetter(value = "extraData") + public String getExtraData() { + return extraData; + } + + @JsonGetter(value = "baseFeePerGas") + public String getBaseFeePerGas() { + return baseFeePerGas; + } + + @JsonGetter(value = "gasLimit") + public String getGasLimit() { + return gasLimit; + } + + @JsonGetter(value = "gasUsed") + public String getGasUsed() { + return gasUsed; + } + + @JsonGetter(value = "timestamp") + public String getTimestamp() { + return timestamp; + } + + @JsonGetter(value = "transactions") + public List getTransactions() { + return transactions; + } + + @JsonGetter(value = "feeRecipient") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getFeeRecipient() { + return feeRecipient; + } + + @JsonGetter(value = "withdrawals") + public List getWithdrawals() { + return withdrawals.orElse(null); + } +} diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java new file mode 100644 index 00000000000..e5b6bd60455 --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java @@ -0,0 +1,127 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; + +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.consensus.merge.MergeContext; +import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; +import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetPayloadResultV2; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockBody; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; + +import java.util.Optional; + +import io.vertx.core.Vertx; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class EngineGetPayloadV2Test { + + private EngineGetPayloadV2 method; + private static final Vertx vertx = Vertx.vertx(); + private static final BlockResultFactory factory = new BlockResultFactory(); + private static final PayloadIdentifier mockPid = + PayloadIdentifier.forPayloadParams( + Hash.ZERO, 1337L, Bytes32.random(), Address.fromHexString("0x42")); + private static final BlockHeader mockHeader = + new BlockHeaderTestFixture().prevRandao(Bytes32.random()).buildHeader(); + private static final Block mockBlock = + new Block(mockHeader, new BlockBody(emptyList(), emptyList(), Optional.of(emptyList()))); + + @Mock private ProtocolContext protocolContext; + + @Mock private MergeContext mergeContext; + @Mock private MergeMiningCoordinator mergeMiningCoordinator; + + @Mock private EngineCallListener engineCallListener; + + @Before + public void before() { + when(mergeContext.retrieveBlockById(mockPid)).thenReturn(Optional.of(mockBlock)); + when(protocolContext.safeConsensusContext(Mockito.any())).thenReturn(Optional.of(mergeContext)); + this.method = + new EngineGetPayloadV2( + vertx, protocolContext, mergeMiningCoordinator, factory, engineCallListener); + } + + @Test + public void shouldReturnExpectedMethodName() { + // will break as specs change, intentional: + assertThat(method.getName()).isEqualTo("engine_getPayloadV2"); + } + + @Test + public void shouldReturnBlockForKnownPayloadId() { + final var resp = resp(mockPid); + assertThat(resp).isInstanceOf(JsonRpcSuccessResponse.class); + Optional.of(resp) + .map(JsonRpcSuccessResponse.class::cast) + .ifPresent( + r -> { + assertThat(r.getResult()).isInstanceOf(EngineGetPayloadResultV2.class); + final EngineGetPayloadResultV2 res = (EngineGetPayloadResultV2) r.getResult(); + assertThat(res.getExecutionPayload().getHash()) + .isEqualTo(mockHeader.getHash().toString()); + assertThat(res.getBlockValue()).isEqualTo(Quantity.create(0)); + assertThat(res.getExecutionPayload().getPrevRandao()) + .isEqualTo(mockHeader.getPrevRandao().map(Bytes32::toString).orElse("")); + }); + verify(engineCallListener, times(1)).executionEngineCalled(); + } + + @Test + public void shouldFailForUnknownPayloadId() { + final var resp = + resp( + PayloadIdentifier.forPayloadParams( + Hash.ZERO, 0L, Bytes32.random(), Address.fromHexString("0x42"))); + assertThat(resp).isInstanceOf(JsonRpcErrorResponse.class); + verify(engineCallListener, times(1)).executionEngineCalled(); + } + + private JsonRpcResponse resp(final PayloadIdentifier pid) { + return method.response( + new JsonRpcRequestContext( + new JsonRpcRequest( + "2.0", + RpcMethod.ENGINE_GET_PAYLOAD_V2.getMethodName(), + new Object[] {pid.serialize()}))); + } +}