Rename executePayload to newPayload and adapt endpoint to Kiln testnet spec

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>
pull/3492/head
Daniel Lehrner 3 years ago committed by GitHub
parent d30b776005
commit 1830d6d95e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinator.java
  2. 2
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeMiningCoordinator.java
  3. 5
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/TransitionCoordinator.java
  4. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java
  5. 107
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayload.java
  6. 52
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineExecutionResult.java
  7. 4
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java
  8. 162
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadTest.java

@ -404,6 +404,11 @@ public class MergeCoordinator implements MergeMiningCoordinator {
return backwardsSyncContext.isSyncing();
}
@Override
public boolean isBackwardSyncing(final Block block) {
return isBackwardSyncing();
}
@Override
public boolean isMiningBeforeMerge() {
return miningParameters.isMiningEnabled();

@ -44,6 +44,8 @@ public interface MergeMiningCoordinator extends MiningCoordinator {
boolean isBackwardSyncing();
boolean isBackwardSyncing(final Block block);
boolean isMiningBeforeMerge();
class ForkchoiceResult {

@ -154,6 +154,11 @@ public class TransitionCoordinator extends TransitionUtils<MiningCoordinator>
return mergeCoordinator.isBackwardSyncing();
}
@Override
public boolean isBackwardSyncing(final Block block) {
return isBackwardSyncing();
}
@Override
public boolean isMiningBeforeMerge() {
return mergeCoordinator.isMiningBeforeMerge();

@ -46,6 +46,7 @@ public enum RpcMethod {
ENGINE_GET_PAYLOAD("engine_getPayloadV1"),
ENGINE_EXECUTE_PAYLOAD("engine_executePayloadV1"),
ENGINE_NEW_PAYLOAD("engine_newPayloadV1"),
ENGINE_FORKCHOICE_UPDATED("engine_forkchoiceUpdatedV1"),
GOQUORUM_ETH_GET_QUORUM_PAYLOAD("eth_getQuorumPayload"),

@ -14,24 +14,25 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.ACCEPTED;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID_TERMINAL_BLOCK;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.SYNCING;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.VALID;
import static org.hyperledger.besu.util.Slf4jLambdaHelper.traceLambda;
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
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.EngineExecutionResult;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EnginePayloadStatusResult;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -53,14 +54,14 @@ import org.apache.tuweni.bytes.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EngineExecutePayload extends ExecutionEngineJsonRpcMethod {
public class EngineNewPayload extends ExecutionEngineJsonRpcMethod {
private static final Hash OMMERS_HASH_CONSTANT = Hash.EMPTY_LIST_HASH;
private static final Logger LOG = LoggerFactory.getLogger(EngineExecutePayload.class);
private static final Logger LOG = LoggerFactory.getLogger(EngineNewPayload.class);
private static final BlockHeaderFunctions headerFunctions = new MainnetBlockHeaderFunctions();
private final MergeMiningCoordinator mergeCoordinator;
public EngineExecutePayload(
public EngineNewPayload(
final Vertx vertx,
final ProtocolContext protocolContext,
final MergeMiningCoordinator mergeCoordinator) {
@ -70,7 +71,7 @@ public class EngineExecutePayload extends ExecutionEngineJsonRpcMethod {
@Override
public String getName() {
return RpcMethod.ENGINE_EXECUTE_PAYLOAD.getMethodName();
return RpcMethod.ENGINE_NEW_PAYLOAD.getMethodName();
}
@Override
@ -80,10 +81,6 @@ public class EngineExecutePayload extends ExecutionEngineJsonRpcMethod {
Object reqId = requestContext.getRequest().getId();
if (mergeContext.isSyncing()) {
return respondWith(reqId, null, SYNCING, null);
}
traceLambda(LOG, "blockparam: {}", () -> Json.encodePrettily(blockParam));
final List<Transaction> transactions;
@ -94,14 +91,19 @@ public class EngineExecutePayload extends ExecutionEngineJsonRpcMethod {
.map(TransactionDecoder::decodeOpaqueBytes)
.collect(Collectors.toList());
} catch (final RLPException | IllegalArgumentException e) {
LOG.warn("failed to decode transactions from newBlock RPC", e);
return respondWith(
return respondWithInvalid(
reqId,
mergeCoordinator.getLatestValidAncestor(blockParam.getParentHash()).orElse(null),
INVALID,
"Failed to decode transactions from block parameter");
}
if (blockParam.getExtraData() == null) {
return respondWithInvalid(
reqId,
mergeCoordinator.getLatestValidAncestor(blockParam.getParentHash()).orElse(null),
"Field extraData must not be null");
}
final BlockHeader newBlockHeader =
new BlockHeader(
blockParam.getParentHash(),
@ -122,60 +124,77 @@ public class EngineExecutePayload extends ExecutionEngineJsonRpcMethod {
0,
headerFunctions);
String errorMessage = null;
// ensure the block hash matches the blockParam hash
// this must be done before any other check
if (!newBlockHeader.getHash().equals(blockParam.getBlockHash())) {
errorMessage =
LOG.debug(
String.format(
"Computed block hash %s does not match block hash parameter %s",
newBlockHeader.getBlockHash(), blockParam.getBlockHash());
newBlockHeader.getBlockHash(), blockParam.getBlockHash()));
return respondWith(reqId, null, INVALID_BLOCK_HASH);
} else {
// do we already have this payload?
// do we already have this payload
if (protocolContext
.getBlockchain()
.getBlockByHash(newBlockHeader.getBlockHash())
.isPresent()) {
LOG.debug("block {} already present", newBlockHeader.getBlockHash());
return respondWith(reqId, blockParam.getBlockHash(), VALID, null);
LOG.debug("block already present");
return respondWith(reqId, blockParam.getBlockHash(), VALID);
}
}
final var latestValidAncestor = mergeCoordinator.getLatestValidAncestor(newBlockHeader);
if (latestValidAncestor.isEmpty()) {
return respondWith(reqId, null, SYNCING, null);
}
if (errorMessage != null) {
return respondWith(reqId, latestValidAncestor.get(), INVALID, errorMessage);
}
// TODO: post-merge cleanup
if (!mergeCoordinator.latestValidAncestorDescendsFromTerminal(newBlockHeader)) {
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.INVALID_TERMINAL_BLOCK);
return respondWith(requestContext.getRequest().getId(), null, INVALID_TERMINAL_BLOCK);
}
final var block =
new Block(newBlockHeader, new BlockBody(transactions, Collections.emptyList()));
if (mergeContext.isSyncing() || mergeCoordinator.isBackwardSyncing(block)) {
return respondWith(reqId, null, SYNCING);
}
final var latestValidAncestor = mergeCoordinator.getLatestValidAncestor(newBlockHeader);
if (latestValidAncestor.isEmpty()) {
return respondWith(reqId, null, ACCEPTED);
}
// execute block and return result response
Result result = mergeCoordinator.executeBlock(block);
if (result.errorMessage.isEmpty()) {
return respondWith(reqId, newBlockHeader.getHash(), VALID, null);
final BlockValidator.Result executionResult = mergeCoordinator.executeBlock(block);
if (executionResult.errorMessage.isEmpty()) {
return respondWith(reqId, newBlockHeader.getHash(), VALID);
} else {
return respondWith(
reqId, latestValidAncestor.get(), INVALID, result.errorMessage.orElse("Unknown reason"));
LOG.debug("New payload is invalid: {}", executionResult.errorMessage.get());
return respondWithInvalid(
reqId, latestValidAncestor.get(), executionResult.errorMessage.get());
}
}
JsonRpcResponse respondWith(
final Object requestId,
final Hash blockHash,
final EngineStatus status,
final String errorMessage) {
final Object requestId, final Hash latestValidHash, final EngineStatus status) {
traceLambda(
LOG,
"Response: requestId: {}, latestValidHash: {}, status: {}",
() -> requestId,
() -> latestValidHash == null ? null : latestValidHash.toHexString(),
status::name);
return new JsonRpcSuccessResponse(
requestId, new EnginePayloadStatusResult(status, latestValidHash, null));
}
JsonRpcResponse respondWithInvalid(
final Object requestId, final Hash latestValidHash, final String validationError) {
traceLambda(
LOG,
"Response: requestId: {}, latestValidHash: {}, status: {}, validationError: {}",
() -> requestId,
() -> latestValidHash == null ? null : latestValidHash.toHexString(),
INVALID::name,
() -> validationError);
return new JsonRpcSuccessResponse(
requestId, new EngineExecutionResult(status, blockHash, errorMessage));
requestId, new EnginePayloadStatusResult(INVALID, latestValidHash, validationError));
}
}

@ -1,52 +0,0 @@
/*
* 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.datatypes.Hash;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus;
import java.util.Optional;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonPropertyOrder({"status", "latestValidHash", "validationError"})
public class EngineExecutionResult {
EngineStatus status;
Optional<Hash> latestValidHash;
Optional<String> validationError;
public EngineExecutionResult(
final EngineStatus status, final Hash latestValidHash, final String validationError) {
this.status = status;
this.latestValidHash = Optional.ofNullable(latestValidHash);
this.validationError = Optional.ofNullable(validationError);
}
@JsonGetter(value = "status")
public String getStatus() {
return status.name();
}
@JsonGetter(value = "latestValidHash")
public String getLatestValidHash() {
return latestValidHash.map(Hash::toHexString).orElse(null);
}
@JsonGetter(value = "validationError")
public String getValidationError() {
return validationError.orElse(null);
}
}

@ -18,9 +18,9 @@ import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineExecutePayload;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdated;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayload;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineNewPayload;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
@ -52,7 +52,7 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods {
Vertx syncVertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(1));
return mapOf(
new EngineGetPayload(syncVertx, protocolContext, blockResultFactory),
new EngineExecutePayload(syncVertx, protocolContext, mergeCoordinator),
new EngineNewPayload(syncVertx, protocolContext, mergeCoordinator),
new EngineForkchoiceUpdated(syncVertx, protocolContext, mergeCoordinator));
}
}

@ -15,11 +15,13 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.ACCEPTED;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID_BLOCK_HASH;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID_TERMINAL_BLOCK;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.SYNCING;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.VALID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@ -35,19 +37,15 @@ 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.parameters.EnginePayloadParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.UnsignedLongParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
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.JsonRpcResponseType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineExecutionResult;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EnginePayloadStatusResult;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
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.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import java.util.Collections;
import java.util.List;
@ -63,8 +61,8 @@ import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class EngineExecutePayloadTest {
private EngineExecutePayload method;
public class EngineNewPayloadTest {
private EngineNewPayload method;
private static final Vertx vertx = Vertx.vertx();
private static final Hash mockHash = Hash.hash(Bytes32.fromHexStringLenient("0x1337deadbeef"));
@ -80,13 +78,13 @@ public class EngineExecutePayloadTest {
public void before() {
when(protocolContext.getConsensusContext(Mockito.any())).thenReturn(mergeContext);
when(protocolContext.getBlockchain()).thenReturn(blockchain);
this.method = new EngineExecutePayload(vertx, protocolContext, mergeCoordinator);
this.method = new EngineNewPayload(vertx, protocolContext, mergeCoordinator);
}
@Test
public void shouldReturnExpectedMethodName() {
// will break as specs change, intentional:
assertThat(method.getName()).isEqualTo("engine_executePayloadV1");
assertThat(method.getName()).isEqualTo("engine_newPayloadV1");
}
@Test
@ -102,10 +100,45 @@ public class EngineExecutePayloadTest {
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
EngineExecutionResult res = fromSuccessResp(resp);
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isEqualTo(mockHeader.getHash().toString());
assertThat(res.getStatus()).isEqualTo(VALID.name());
assertThat(res.getValidationError()).isNull();
assertThat(res.getError()).isNull();
}
@Test
public void shouldReturnInvalidOnBlockExecutionError() {
BlockHeader mockHeader = new BlockHeaderTestFixture().baseFeePerGas(Wei.ONE).buildHeader();
when(blockchain.getBlockByHash(any())).thenReturn(Optional.empty());
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
.thenReturn(Optional.of(mockHash));
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
when(mergeCoordinator.executeBlock(any())).thenReturn(new Result("error 42"));
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isEqualTo(mockHash.toString());
assertThat(res.getStatus()).isEqualTo(INVALID.name());
assertThat(res.getError()).isEqualTo("error 42");
}
@Test
public void shouldReturnAcceptedOnLatestValidAncestorEmpty() {
BlockHeader mockHeader = new BlockHeaderTestFixture().baseFeePerGas(Wei.ONE).buildHeader();
when(blockchain.getBlockByHash(any())).thenReturn(Optional.empty());
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
.thenReturn(Optional.empty());
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isNull();
assertThat(res.getStatus()).isEqualTo(ACCEPTED.name());
assertThat(res.getError()).isNull();
}
@Test
@ -118,56 +151,37 @@ public class EngineExecutePayloadTest {
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
EngineExecutionResult res = fromSuccessResp(resp);
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isEqualTo(mockHeader.getHash().toString());
assertThat(res.getStatus()).isEqualTo(VALID.name());
assertThat(res.getValidationError()).isNull();
assertThat(res.getError()).isNull();
}
@Test
public void shouldReturnErrorOnInvalidTerminalBlock() {
public void shouldReturnInvalidTerminalBlock() {
BlockHeader mockHeader = new BlockHeaderTestFixture().baseFeePerGas(Wei.ONE).buildHeader();
when(blockchain.getBlockByHash(any())).thenReturn(Optional.empty());
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
.thenReturn(Optional.of(mockHash));
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(false);
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
assertThat(resp.getType()).isEqualTo(JsonRpcResponseType.ERROR);
JsonRpcErrorResponse res = ((JsonRpcErrorResponse) resp);
assertThat(res.getError()).isEqualTo(JsonRpcError.INVALID_TERMINAL_BLOCK);
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isNull();
assertThat(res.getStatus()).isEqualTo(INVALID_TERMINAL_BLOCK.name());
}
@Test
public void shouldReturnInvalidOnBadHashParameter() {
public void shouldReturnInvalidBlockHashOnBadHashParameter() {
BlockHeader mockHeader = new BlockHeaderTestFixture().buildHeader();
// exploit the hash difference between basefee of Optional.empty vs Wei.ZERO (deserialized
// value):
BlockHeader realHeader =
BlockHeaderBuilder.fromHeader(mockHeader)
.baseFee(Wei.ZERO)
.blockHeaderFunctions(new MainnetBlockHeaderFunctions())
.buildBlockHeader();
// when(blockchain.getBlockByHash(any())).thenReturn(Optional.empty());
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
.thenReturn(Optional.of(mockHash));
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
EngineExecutionResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isEqualTo(mockHash.toString());
assertThat(res.getStatus()).isEqualTo(INVALID.name());
assertThat(res.getValidationError())
.isEqualTo(
String.format(
"Computed block hash %s does not match block hash parameter %s",
realHeader.getBlockHash(), mockHeader.getBlockHash()));
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isNull();
assertThat(res.getStatus()).isEqualTo(INVALID_BLOCK_HASH.name());
}
@Test
@ -175,60 +189,57 @@ public class EngineExecutePayloadTest {
BlockHeader realHeader = new BlockHeaderTestFixture().baseFeePerGas(Wei.ONE).buildHeader();
BlockHeader paramHeader = spy(realHeader);
when(paramHeader.getHash()).thenReturn(Hash.fromHexStringLenient("0x1337"));
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
.thenReturn(Optional.of(mockHash));
var resp = resp(mockPayload(paramHeader, Collections.emptyList()));
EngineExecutionResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isEqualTo(mockHash.toString());
assertThat(res.getStatus()).isEqualTo(INVALID.name());
assertThat(res.getValidationError())
.isEqualTo(
String.format(
"Computed block hash %s does not match block hash parameter %s",
realHeader.getBlockHash(), paramHeader.getHash()));
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isNull();
assertThat(res.getStatus()).isEqualTo(INVALID_BLOCK_HASH.name());
}
@Test
public void shouldReturnInvalidOnMalformedTransactions() {
BlockHeader mockHeader = new BlockHeaderTestFixture().buildHeader();
// when(blockchain.getBlockByHash(any())).thenReturn(Optional.empty());
when(mergeCoordinator.getLatestValidAncestor(any(Hash.class)))
.thenReturn(Optional.of(mockHash));
var resp = resp(mockPayload(mockHeader, List.of("0xDEAD", "0xBEEF")));
EngineExecutionResult res = fromSuccessResp(resp);
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isEqualTo(mockHash.toString());
assertThat(res.getStatus()).isEqualTo(INVALID.name());
assertThat(res.getValidationError())
.isEqualTo("Failed to decode transactions from block parameter");
assertThat(res.getError()).isEqualTo("Failed to decode transactions from block parameter");
}
@Test
public void shouldRespondWithSyncingDuringForwardSync() {
BlockHeader mockHeader = new BlockHeaderTestFixture().baseFeePerGas(Wei.ONE).buildHeader();
when(blockchain.getBlockByHash(any())).thenReturn(Optional.empty());
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
when(mergeContext.isSyncing()).thenReturn(Boolean.TRUE);
var resp = resp(mock(EnginePayloadParameter.class));
EngineExecutionResult res = fromSuccessResp(resp);
assertThat(res.getValidationError()).isNull();
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getError()).isNull();
assertThat(res.getStatus()).isEqualTo(SYNCING.name());
assertThat(res.getLatestValidHash()).isNull();
}
@Test
public void shouldRespondWithSyncingDuringBackwardsSync() {
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
.thenReturn(Optional.empty());
BlockHeader mockHeader = new BlockHeaderTestFixture().buildHeader();
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
when(mergeCoordinator.isBackwardSyncing(any(Block.class))).thenReturn(Boolean.TRUE);
BlockHeader mockHeader = new BlockHeaderTestFixture().baseFeePerGas(Wei.ONE).buildHeader();
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
EngineExecutionResult res = fromSuccessResp(resp);
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isNull();
assertThat(res.getStatus()).isEqualTo(SYNCING.name());
assertThat(res.getValidationError()).isNull();
assertThat(res.getError()).isNull();
}
@Test
@ -237,6 +248,21 @@ public class EngineExecutePayloadTest {
assertThat(mergeContext.getTerminalTotalDifficulty()).isNull();
}
@Test
public void shouldRespondWithInvalidIfExtraDataIsNull() {
BlockHeader realHeader = new BlockHeaderTestFixture().baseFeePerGas(Wei.ONE).buildHeader();
BlockHeader paramHeader = spy(realHeader);
when(paramHeader.getHash()).thenReturn(Hash.fromHexStringLenient("0x1337"));
when(paramHeader.getExtraData().toHexString()).thenReturn(null);
var resp = resp(mockPayload(paramHeader, Collections.emptyList()));
EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isNull();
assertThat(res.getStatus()).isEqualTo(INVALID.name());
assertThat(res.getError()).isEqualTo("Field extraData must not be null");
}
private JsonRpcResponse resp(final EnginePayloadParameter payload) {
return method.response(
new JsonRpcRequestContext(
@ -255,19 +281,19 @@ public class EngineExecutePayloadTest {
new UnsignedLongParameter(header.getGasLimit()),
new UnsignedLongParameter(header.getGasUsed()),
new UnsignedLongParameter(header.getTimestamp()),
header.getExtraData().toHexString(),
header.getExtraData() == null ? null : header.getExtraData().toHexString(),
header.getReceiptsRoot(),
header.getLogsBloom(),
header.getRandom().map(Bytes32::toHexString).orElse("0x0"),
txs);
}
private EngineExecutionResult fromSuccessResp(final JsonRpcResponse resp) {
private EnginePayloadStatusResult fromSuccessResp(final JsonRpcResponse resp) {
assertThat(resp.getType()).isEqualTo(JsonRpcResponseType.SUCCESS);
return Optional.of(resp)
.map(JsonRpcSuccessResponse.class::cast)
.map(JsonRpcSuccessResponse::getResult)
.map(EngineExecutionResult.class::cast)
.map(EnginePayloadStatusResult.class::cast)
.get();
}
}
Loading…
Cancel
Save