Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure block height manager is restarted when BFT coordinator is #8308

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
### Bug fixes
- Upgrade Netty to version 4.1.118 to fix CVE-2025-24970 [#8275](https://github.com/hyperledger/besu/pull/8275)
- Add missing RPC method `debug_accountRange` to `RpcMethod.java` and implemented its handler. [#8153](https://github.com/hyperledger/besu/issues/8153)
- Fix issue with new QBFT/IBFT blocks being produced under certain circumstances. [#8308](https://github.com/hyperledger/besu/issues/8308)

## 25.2.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public void stop() {
LOG.debug("Interrupted while waiting for BftProcessor to stop.", e);
Thread.currentThread().interrupt();
}
eventHandler.reset();
Copy link
Contributor

Choose a reason for hiding this comment

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

Reset seems odd here in that we trying to stop all processing of BFT here. A stop method to prevent any more processing in the controller would make sense. With everything stopped, we won't be processing BFT events anyway so maybe it's not worth it?

And by a stop method on the controller I'm thinking it would change the started state to false as it does right now in the reset method and also replace the block height manager with the noop version so it wouldn't do any processing of events.

bftExecutors.stop();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ protected BaseBftController(
public void start() {
if (started.compareAndSet(false, true)) {
startNewHeightManager(blockchain.getChainHeadHeader());
} else {
// In normal circumstances the height manager should only be started once. If the caller
// has stopped the height manager (e.g. while sync completes) they must call reset() before
// starting the height manager again.
throw new IllegalStateException("Attempt to start new height manager without resetting");
}
}

@Override
public void reset() {
if (started.compareAndSet(true, false)) {
LOG.debug("Height manager reset");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public interface BftEventHandler {
/** Start. */
void start();

/** Reset. */
void reset();

/**
* Handle message event.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.consensus.ibft.statemachine;

import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.util.Lists.newArrayList;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
Expand Down Expand Up @@ -478,4 +480,21 @@ private void setupRoundChange(
when(roundChangeMessageData.decode()).thenReturn(roundChange);
roundChangeMessage = new DefaultMessage(null, roundChangeMessageData);
}

@Test
public void heightManagerCanOnlyBeStartedOnceWithoutReset() {
constructIbftController();
ibftController.start();
assertThatThrownBy(() -> ibftController.start())
.isInstanceOf(IllegalStateException.class)
.hasMessage("Attempt to start new height manager without resetting");
}

@Test
public void heightManagerCanBeRestartedIfReset() {
constructIbftController();
ibftController.start();
ibftController.reset();
assertThatNoException().isThrownBy(() -> ibftController.start());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,18 @@ private BaseQbftBlockHeightManager getCurrentHeightManager() {
public void start() {
if (started.compareAndSet(false, true)) {
startNewHeightManager(blockchain.getChainHeadHeader());
} else {
// In normal circumstances the height manager should only be started once. If the caller
// has stopped the height manager (e.g. while sync completes) they must call reset() before
// starting the height manager again.
throw new IllegalStateException("Attempt to start new height manager without resetting");
}
}

@Override
public void reset() {
if (started.compareAndSet(true, false)) {
LOG.debug("QBFT height manager reset");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public interface QbftEventHandler {
/** Start. */
void start();

/** Reset. */
void reset();

/**
* Handle errorMessage event.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.consensus.qbft.core.statemachine;

import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.util.Lists.newArrayList;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
Expand Down Expand Up @@ -484,4 +486,21 @@ private void setupRoundChange(
when(roundChangeMessageData.decode(blockEncoder)).thenReturn(roundChange);
roundChangeMessage = new DefaultMessage(null, roundChangeMessageData);
}

@Test
public void heightManagerCanOnlyBeStartedOnceWithoutReset() {
constructQbftController();
qbftController.start();
assertThatThrownBy(() -> qbftController.start())
.isInstanceOf(IllegalStateException.class)
.hasMessage("Attempt to start new height manager without resetting");
}

@Test
public void heightManagerCanBeRestartedIfReset() {
constructQbftController();
qbftController.start();
qbftController.reset();
assertThatNoException().isThrownBy(() -> qbftController.start());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public void start() {
qbftEventHandler.start();
}

@Override
public void reset() {
qbftEventHandler.reset();
}

@Override
public void handleMessageEvent(final BftReceivedMessageEvent msg) {
qbftEventHandler.handleMessageEvent(msg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ void startDelegatesToQbftEventHandler() {
verify(qbftEventHandler).start();
}

@Test
void resetDelegatesToQbftEventHandler() {
handler.reset();
verify(qbftEventHandler).reset();
}

@Test
void handleMessageEventDelegatesToQbftEventHandler() {
handler.handleMessageEvent(bftReceivedMessageEvent);
Expand Down