Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
}

// TODO: post-merge cleanup, this should be unnecessary after merge
if (!mergeContext.get().isCheckpointPostMergeSync()
if (requireTerminalPoWBlockValidation()
&& !mergeContext.get().isCheckpointPostMergeSync()
&& !mergeCoordinator.latestValidAncestorDescendsFromTerminal(newHead)
&& !mergeContext.get().isChainPruningEnabled()) {
logForkchoiceUpdatedCall(INVALID, forkChoice);
Expand Down Expand Up @@ -154,7 +155,7 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
"Invalid payload attributes: {}",
() ->
maybePayloadAttributes.map(EnginePayloadAttributesParameter::serialize).orElse(null));
return new JsonRpcErrorResponse(requestId, JsonRpcError.INVALID_PAYLOAD_ATTRIBUTES);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see this change mentioned in ethereum/execution-apis#338 - is this something different?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, bring some changes from ethereum/execution-apis#337 too. Sorry should have updated the description.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my bad, I got Zhenyang to crowbar that change in too :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok makes sense

return new JsonRpcErrorResponse(requestId, getInvalidPayloadError());
}

if (!result.isValid()) {
Expand Down Expand Up @@ -217,9 +218,6 @@ private JsonRpcResponse handleNonValidForkchoiceUpdate(
new EngineUpdateForkchoiceResult(
INVALID, latestValid.orElse(null), null, result.getErrorMessage()));
break;
case INVALID_PAYLOAD_ATTRIBUTES:
response = new JsonRpcErrorResponse(requestId, JsonRpcError.INVALID_PAYLOAD_ATTRIBUTES);
break;
case IGNORE_UPDATE_TO_OLD_HEAD:
response =
new JsonRpcSuccessResponse(
Expand Down Expand Up @@ -300,6 +298,14 @@ private JsonRpcResponse syncingResponse(
requestId, new EngineUpdateForkchoiceResult(SYNCING, null, null, Optional.empty()));
}

protected boolean requireTerminalPoWBlockValidation() {
return false;
}

protected JsonRpcError getInvalidPayloadError() {
return JsonRpcError.INVALID_PARAMS;
}

// fcU calls are synchronous, no need to make volatile
private long lastFcuInfoLog = System.currentTimeMillis();
private static final String logMessage =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
"Computed block hash %s does not match block hash parameter %s",
newBlockHeader.getBlockHash(), blockParam.getBlockHash());
LOG.debug(errorMessage);
return respondWithInvalid(reqId, blockParam, null, INVALID_BLOCK_HASH, errorMessage);
return respondWithInvalid(reqId, blockParam, null, getInvalidBlockHashStatus(), errorMessage);
}
// do we already have this payload
if (protocolContext.getBlockchain().getBlockByHash(newBlockHeader.getBlockHash()).isPresent()) {
Expand Down Expand Up @@ -208,7 +208,8 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
}

// TODO: post-merge cleanup
if (!mergeContext.get().isCheckpointPostMergeSync()
if (requireTerminalPoWBlockValidation()
Comment thread
jframe marked this conversation as resolved.
&& !mergeContext.get().isCheckpointPostMergeSync()
&& !mergeCoordinator.latestValidAncestorDescendsFromTerminal(newBlockHeader)
&& !mergeContext.get().isChainPruningEnabled()) {
mergeCoordinator.addBadBlock(block, Optional.empty());
Expand Down Expand Up @@ -308,6 +309,14 @@ JsonRpcResponse respondWithInvalid(
invalidStatus, latestValidHash, Optional.of(validationError)));
}

protected boolean requireTerminalPoWBlockValidation() {
return false;
}

protected EngineStatus getInvalidBlockHashStatus() {
return INVALID;
}

private void logImportedBlockInfo(final Block block, final double timeInS) {
LOG.info(
String.format(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.mainnet.TimestampSchedule;

import io.vertx.core.Vertx;
Expand All @@ -36,4 +37,14 @@ public EngineForkchoiceUpdatedV1(
public String getName() {
return RpcMethod.ENGINE_FORKCHOICE_UPDATED_V1.getMethodName();
}

@Override
protected boolean requireTerminalPoWBlockValidation() {
return true;
}

@Override
protected JsonRpcError getInvalidPayloadError() {
return JsonRpcError.INVALID_PAYLOAD_ATTRIBUTES;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;

import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID_BLOCK_HASH;

import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
Expand All @@ -39,4 +41,14 @@ public EngineNewPayloadV1(
public String getName() {
return RpcMethod.ENGINE_NEW_PAYLOAD_V1.getMethodName();
}

@Override
protected boolean requireTerminalPoWBlockValidation() {
Comment thread
jframe marked this conversation as resolved.
return true;
}

@Override
protected EngineStatus getInvalidBlockHashStatus() {
return INVALID_BLOCK_HASH;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ public void shouldReturnSyncingIfMissingNewHead() {

@Test
public void shouldReturnInvalidOnBadTerminalBlock() {
if (!validateTerminalPoWBlock()) return;
BlockHeader mockHeader = blockHeaderBuilder.buildHeader();

when(mergeCoordinator.getOrSyncHeadByHash(mockHeader.getHash(), Hash.ZERO))
Expand Down Expand Up @@ -181,7 +182,8 @@ public void shouldReturnValidWithoutFinalizedOrPayload() {
BlockHeader mockHeader = blockHeaderBuilder.buildHeader();
when(mergeCoordinator.getOrSyncHeadByHash(mockHeader.getHash(), Hash.ZERO))
.thenReturn(Optional.of(mockHeader));
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(mockHeader)).thenReturn(true);
if (validateTerminalPoWBlock())
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(mockHeader)).thenReturn(true);

assertSuccessWithPayloadForForkchoiceResult(
new EngineForkchoiceUpdatedParameter(mockHeader.getHash(), Hash.ZERO, Hash.ZERO),
Expand All @@ -199,7 +201,8 @@ public void shouldReturnInvalidOnOldTimestamp() {
.timestamp(parent.getTimestamp())
.buildHeader();
when(blockchain.getBlockHeader(parent.getHash())).thenReturn(Optional.of(parent));
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(mockHeader)).thenReturn(true);
if (validateTerminalPoWBlock())
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(mockHeader)).thenReturn(true);
when(mergeCoordinator.isDescendantOf(any(), any())).thenReturn(true);
when(mergeContext.isSyncing()).thenReturn(false);
when(mergeCoordinator.getOrSyncHeadByHash(mockHeader.getHash(), parent.getHash()))
Expand Down Expand Up @@ -244,7 +247,8 @@ public void shouldReturnValidWithoutFinalizedWithPayload() {
BlockHeader mockHeader = blockHeaderBuilder.buildHeader();
when(mergeCoordinator.getOrSyncHeadByHash(mockHeader.getHash(), Hash.ZERO))
.thenReturn(Optional.of(mockHeader));
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(mockHeader)).thenReturn(true);
if (validateTerminalPoWBlock())
Comment thread
siladu marked this conversation as resolved.
Outdated
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(mockHeader)).thenReturn(true);

var payloadParams =
new EnginePayloadAttributesParameter(
Expand Down Expand Up @@ -427,7 +431,8 @@ public void shouldIgnoreUpdateToOldHeadAndNotPreparePayload() {

when(mergeCoordinator.getOrSyncHeadByHash(mockHeader.getHash(), Hash.ZERO))
.thenReturn(Optional.of(mockHeader));
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(mockHeader)).thenReturn(true);
if (validateTerminalPoWBlock())
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(mockHeader)).thenReturn(true);

var ignoreOldHeadUpdateRes = ForkchoiceResult.withIgnoreUpdateToOldHead(mockHeader);
when(mergeCoordinator.updateForkChoice(any(), any(), any())).thenReturn(ignoreOldHeadUpdateRes);
Expand Down Expand Up @@ -481,7 +486,7 @@ public void shouldReturnInvalidIfPayloadTimestampNotGreaterThanHead() {
mockHeader.getHash(), Hash.ZERO, mockParent.getHash()),
Optional.of(payloadParams));

assertInvalidForkchoiceState(resp, JsonRpcError.INVALID_PAYLOAD_ATTRIBUTES);
assertInvalidForkchoiceState(resp, expectedInvalidPayloadError());
verify(engineCallListener, times(1)).executionEngineCalled();
}

Expand All @@ -505,7 +510,7 @@ public void shouldReturnInvalidIfWithdrawalsIsNotNull_WhenWithdrawalsProhibited(
mockHeader.getHash(), Hash.ZERO, mockParent.getHash()),
Optional.of(payloadParams));

assertInvalidForkchoiceState(resp, JsonRpcError.INVALID_PAYLOAD_ATTRIBUTES);
assertInvalidForkchoiceState(resp, expectedInvalidPayloadError());
verify(engineCallListener, times(1)).executionEngineCalled();
}

Expand Down Expand Up @@ -569,7 +574,7 @@ public void shouldReturnInvalidIfWithdrawalsIsNull_WhenWithdrawalsAllowed() {
mockHeader.getHash(), Hash.ZERO, mockParent.getHash()),
Optional.of(payloadParams));

assertInvalidForkchoiceState(resp, JsonRpcError.INVALID_PAYLOAD_ATTRIBUTES);
assertInvalidForkchoiceState(resp, expectedInvalidPayloadError());
verify(engineCallListener, times(1)).executionEngineCalled();
}

Expand Down Expand Up @@ -667,7 +672,8 @@ private void setupValidForkchoiceUpdate(final BlockHeader mockHeader) {
when(blockchain.getBlockHeader(any())).thenReturn(Optional.of(mockHeader));
when(mergeCoordinator.getOrSyncHeadByHash(mockHeader.getHash(), Hash.ZERO))
.thenReturn(Optional.of(mockHeader));
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(mockHeader)).thenReturn(true);
if (validateTerminalPoWBlock())
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(mockHeader)).thenReturn(true);
when(mergeCoordinator.isDescendantOf(any(), any())).thenReturn(true);
}

Expand Down Expand Up @@ -724,6 +730,14 @@ private EngineUpdateForkchoiceResult assertSuccessWithPayloadForForkchoiceResult
return res;
}

protected boolean validateTerminalPoWBlock() {
return false;
}

protected JsonRpcError expectedInvalidPayloadError() {
return JsonRpcError.INVALID_PARAMS;
}

private JsonRpcResponse resp(
final EngineForkchoiceUpdatedParameter forkchoiceParam,
final Optional<EnginePayloadAttributesParameter> payloadParam) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
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.SYNCING;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.VALID;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameterTestFixture.WITHDRAWAL_PARAM_1;
Expand All @@ -42,6 +41,7 @@
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.methods.ExecutionEngineJsonRpcMethod;
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.parameters.WithdrawalParameter;
Expand Down Expand Up @@ -169,8 +169,9 @@ public void shouldReturnAcceptedOnLatestValidAncestorEmpty() {
.thenReturn(Optional.of(mock(BlockHeader.class)));
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
.thenReturn(Optional.empty());
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
if (validateTerminalPoWBlock())
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);

var resp = resp(mockPayload(mockHeader, Collections.emptyList()));

Expand Down Expand Up @@ -198,6 +199,7 @@ public void shouldReturnSuccessOnAlreadyPresent() {

@Test
public void shouldReturnInvalidOnBadTerminalBlock() {
if (!validateTerminalPoWBlock()) return;
Comment thread
siladu marked this conversation as resolved.
Outdated
BlockHeader mockHeader = createBlockHeader();
when(blockchain.getBlockByHash(mockHeader.getHash())).thenReturn(Optional.empty());
when(blockchain.getBlockHeader(mockHeader.getParentHash()))
Expand Down Expand Up @@ -268,8 +270,9 @@ public void shouldNotReturnInvalidOnThrownMerkleTrieException() {
.thenReturn(Optional.of(mock(BlockHeader.class)));
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
.thenReturn(Optional.of(mockHash));
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
if (validateTerminalPoWBlock())
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
when(mergeCoordinator.rememberBlock(any())).thenThrow(new MerkleTrieException("missing leaf"));

var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
Expand All @@ -288,7 +291,7 @@ public void shouldReturnInvalidBlockHashOnBadHashParameter() {

EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isEmpty();
assertThat(res.getStatusAsString()).isEqualTo(INVALID_BLOCK_HASH.name());
assertThat(res.getStatusAsString()).isEqualTo(getCorrectInvalidBlockHashStatus().name());
verify(engineCallListener, times(1)).executionEngineCalled();
}

Expand All @@ -302,7 +305,7 @@ public void shouldCheckBlockValidityBeforeCheckingByHashForExisting() {

EnginePayloadStatusResult res = fromSuccessResp(resp);
assertThat(res.getLatestValidHash()).isEmpty();
assertThat(res.getStatusAsString()).isEqualTo(INVALID_BLOCK_HASH.name());
assertThat(res.getStatusAsString()).isEqualTo(getCorrectInvalidBlockHashStatus().name());
verify(engineCallListener, times(1)).executionEngineCalled();
}

Expand Down Expand Up @@ -526,12 +529,21 @@ private BlockHeader setupValidPayload(final BlockProcessingResult value) {
.thenReturn(Optional.of(mock(BlockHeader.class)));
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
.thenReturn(Optional.of(mockHash));
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
if (validateTerminalPoWBlock())
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
when(mergeCoordinator.rememberBlock(any())).thenReturn(value);
return mockHeader;
}

protected boolean validateTerminalPoWBlock() {
return false;
}

protected ExecutionEngineJsonRpcMethod.EngineStatus getCorrectInvalidBlockHashStatus() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer 'expected' over 'correct' like your other tests: expectedInvalidBlockHashStatus

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or just getInvalidBlockHashStatus

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
protected ExecutionEngineJsonRpcMethod.EngineStatus getCorrectInvalidBlockHashStatus() {
protected ExecutionEngineJsonRpcMethod.EngineStatus getInvalidBlockHashStatus() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer expected too - since we expect different status for the same error for V1 and V2.

return INVALID;
}

private EnginePayloadStatusResult fromSuccessResp(final JsonRpcResponse resp) {
assertThat(resp.getType()).isEqualTo(JsonRpcResponseType.SUCCESS);
return Optional.of(resp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static org.assertj.core.api.Assertions.assertThat;

import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;

import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -39,4 +40,14 @@ public void shouldReturnExpectedMethodName() {
protected String getMethodName() {
return RpcMethod.ENGINE_FORKCHOICE_UPDATED_V1.getMethodName();
}

@Override
protected boolean validateTerminalPoWBlock() {
return true;
}

@Override
protected JsonRpcError expectedInvalidPayloadError() {
return JsonRpcError.INVALID_PAYLOAD_ATTRIBUTES;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
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.INVALID_BLOCK_HASH;

import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod;

import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -32,4 +35,14 @@ public EngineNewPayloadV1Test() {
public void shouldReturnExpectedMethodName() {
assertThat(method.getName()).isEqualTo("engine_newPayloadV1");
}

@Override
protected boolean validateTerminalPoWBlock() {
return true;
}

@Override
protected ExecutionEngineJsonRpcMethod.EngineStatus getCorrectInvalidBlockHashStatus() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
protected ExecutionEngineJsonRpcMethod.EngineStatus getCorrectInvalidBlockHashStatus() {
protected ExecutionEngineJsonRpcMethod.EngineStatus getInvalidBlockHashStatus() {

return INVALID_BLOCK_HASH;
}
}