diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c9631fedf..9e7b1f21ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Return the correct latest valid hash in case of bad block when calling engine methods [#4056](https://github.com/hyperledger/besu/pull/4056) - Add a PoS block header rule to check that the current block is more recent than its parent [#4066](https://github.com/hyperledger/besu/pull/4066) - Fixed a trie log layer issue on bonsai during reorg [#4069](https://github.com/hyperledger/besu/pull/4069) +- Fix transition protocol schedule to return the pre Merge schedule when reorg pre TTD [#4078](https://github.com/hyperledger/besu/pull/4078) ## 22.7.0-RC1 diff --git a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java index 5c05a50104..75e03ae4df 100644 --- a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java +++ b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java @@ -86,8 +86,7 @@ public class TransitionProtocolSchedule extends TransitionUtils thisDifficulty); // if this block is pre-merge or a TTD block - if ((thisDifficulty.lessOrEqualThan(terminalDifficulty) - && thisDifficulty.greaterThan(parentDifficulty)) + if (thisDifficulty.lessThan(terminalDifficulty) || TransitionUtils.isTerminalProofOfWorkBlock(blockHeader, protocolContext)) { debugLambda( LOG, diff --git a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/TransitionProtocolScheduleTest.java b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/TransitionProtocolScheduleTest.java new file mode 100644 index 0000000000..66aa2a31ce --- /dev/null +++ b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/TransitionProtocolScheduleTest.java @@ -0,0 +1,150 @@ +/* + * 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.consensus.merge; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.Difficulty; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; + +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class TransitionProtocolScheduleTest { + + @Mock ProtocolContext protocolContext; + @Mock MutableBlockchain blockchain; + @Mock MergeContext mergeContext; + @Mock ProtocolSchedule preMergeProtocolSchedule; + @Mock ProtocolSchedule postMergeProtocolSchedule; + @Mock BlockHeader blockHeader; + + private static final Difficulty TTD = Difficulty.of(100L); + private static final long BLOCK_NUMBER = 29L; + private TransitionProtocolSchedule transitionProtocolSchedule; + + @Before + public void setUp() { + when(protocolContext.getBlockchain()).thenReturn(blockchain); + when(protocolContext.getConsensusContext(MergeContext.class)).thenReturn(mergeContext); + when(mergeContext.getTerminalTotalDifficulty()).thenReturn(TTD); + + transitionProtocolSchedule = + new TransitionProtocolSchedule( + preMergeProtocolSchedule, postMergeProtocolSchedule, mergeContext); + } + + @Test + public void returnPostMergeIfFinalizedExists() { + when(mergeContext.getFinalized()).thenReturn(Optional.of(mock(BlockHeader.class))); + when(blockHeader.getNumber()).thenReturn(BLOCK_NUMBER); + + transitionProtocolSchedule.getByBlockHeader(protocolContext, blockHeader); + + verifyPostMergeProtocolScheduleReturned(); + } + + @Test + public void returnPreMergeIfBeforeMerge() { + when(mergeContext.getFinalized()).thenReturn(Optional.empty()); + when(mergeContext.isPostMerge()).thenReturn(false); + + when(blockHeader.getNumber()).thenReturn(BLOCK_NUMBER); + + transitionProtocolSchedule.getByBlockHeader(protocolContext, blockHeader); + + verifyPreMergeProtocolScheduleReturned(); + } + + @Test + public void returnPreMergeIfTerminalPoWBlock() { + + when(mergeContext.getFinalized()).thenReturn(Optional.empty()); + when(mergeContext.isPostMerge()).thenReturn(true); + + final Hash parentHash = Hash.fromHexStringLenient("0xabc123"); + + when(blockHeader.getNumber()).thenReturn(BLOCK_NUMBER); + when(blockHeader.getParentHash()).thenReturn(parentHash); + when(blockHeader.getDifficulty()).thenReturn(Difficulty.of(10L)); + when(blockchain.getTotalDifficultyByHash(parentHash)) + .thenReturn(Optional.of(Difficulty.of(95L))); + + transitionProtocolSchedule.getByBlockHeader(protocolContext, blockHeader); + + verifyPreMergeProtocolScheduleReturned(); + } + + @Test + public void returnPreMergeIfAfterMergeButReorgPreTTD() { + + when(mergeContext.getFinalized()).thenReturn(Optional.empty()); + when(mergeContext.isPostMerge()).thenReturn(true); + + final Hash parentHash = Hash.fromHexStringLenient("0xabc123"); + + when(blockHeader.getNumber()).thenReturn(BLOCK_NUMBER); + when(blockHeader.getParentHash()).thenReturn(parentHash); + when(blockHeader.getDifficulty()).thenReturn(Difficulty.of(2L)); + when(blockchain.getTotalDifficultyByHash(parentHash)) + .thenReturn(Optional.of(Difficulty.of(95L))); + + transitionProtocolSchedule.getByBlockHeader(protocolContext, blockHeader); + + verifyPreMergeProtocolScheduleReturned(); + } + + @Test + public void returnPostMergeIfAfterMergeButReorgPostTTD() { + + when(mergeContext.getFinalized()).thenReturn(Optional.empty()); + when(mergeContext.isPostMerge()).thenReturn(true); + + final Hash parentHash = Hash.fromHexStringLenient("0xabc123"); + + when(blockHeader.getNumber()).thenReturn(BLOCK_NUMBER); + when(blockHeader.getParentHash()).thenReturn(parentHash); + when(blockHeader.getDifficulty()).thenReturn(Difficulty.ZERO); + when(blockchain.getTotalDifficultyByHash(parentHash)) + .thenReturn(Optional.of(Difficulty.of(105L))); + + transitionProtocolSchedule.getByBlockHeader(protocolContext, blockHeader); + + verifyPostMergeProtocolScheduleReturned(); + } + + private void verifyPreMergeProtocolScheduleReturned() { + verify(preMergeProtocolSchedule).getByBlockNumber(BLOCK_NUMBER); + verifyNoInteractions(postMergeProtocolSchedule); + } + + private void verifyPostMergeProtocolScheduleReturned() { + verify(postMergeProtocolSchedule).getByBlockNumber(BLOCK_NUMBER); + verifyNoInteractions(preMergeProtocolSchedule); + } +}