QBFT - Select correct ValidatorProvider when transitioning from validator contract to block header mode #2868
Description
Description
During the transition from contract mode to block header mode, when creating the extra data for a new block, we need to look ahead to the next block's validatorProvider to obtain the nonEmpty voteProvider associated with the BlockValidatorProvider.
Acceptance Criteria
Given we are transitioning from contract to block header mode
When a block’s extra data is created
Then it should select the correct validatorProvider and not error upon selecting the TransactionValidatorProvider with an empty voteProvider.
(The following is a more robust AC which gives better asserts in an automated test than the above AC's "ensure it doesn't error")
Given we are transitioning from contract to block header mode
When a block’s extra data is created
Then it should use the same validators as the previous block (which will be the list of validators currently in the contract)
Steps to Reproduce (Bug)
- Start a node using a transition config like below:
"transitions": {
"qbft": [
{
"block": 2,
"validatorselectionmode": "contract",
"validatorcontractaddress": "0x0000000000000000000000000000000000008888"
},
{
"block": 4,
"validatorselectionmode": "blockheader"
}
]
}
- Wait for a block to be mined for the transition back to block header mode
- See the block creation fail due to this error:
2021-10-06 21:10:01.007+02:00 | pool-8-thread-1 | ERROR | EventMultiplexer | State machine threw exception while processing event \{BlockTimerExpiry{Round Identifier=ConsensusRoundIdentifier{Sequence=4, Round=0}}\}
java.lang.IllegalStateException: Bft requires a vote provider
at com.google.common.base.Preconditions.checkState(Preconditions.java:510)
at org.hyperledger.besu.consensus.common.bft.blockcreation.BftBlockCreatorFactory.createExtraData(BftBlockCreatorFactory.java:108)
at org.hyperledger.besu.consensus.qbft.blockcreation.QbftBlockCreatorFactory.createExtraData(QbftBlockCreatorFactory.java:93)
at org.hyperledger.besu.consensus.common.bft.blockcreation.BftBlockCreatorFactory.lambda$create$1(BftBlockCreatorFactory.java:82)
at org.hyperledger.besu.ethereum.blockcreation.AbstractBlockCreator.createBlock(AbstractBlockCreator.java:182)
at org.hyperledger.besu.ethereum.blockcreation.AbstractBlockCreator.createBlock(AbstractBlockCreator.java:126)
at org.hyperledger.besu.consensus.qbft.statemachine.QbftRound.createAndSendProposalMessage(QbftRound.java:100)
at org.hyperledger.besu.consensus.qbft.statemachine.QbftBlockHeightManager.handleBlockTimerExpiry(QbftBlockHeightManager.java:125)
at org.hyperledger.besu.consensus.common.bft.statemachine.BaseBftController.handleBlockTimerExpiry(BaseBftController.java:140)
at org.hyperledger.besu.consensus.common.bft.EventMultiplexer.handleBftEvent(EventMultiplexer.java:54)
at java.base/java.util.Optional.ifPresent(Optional.java:183)
at org.hyperledger.besu.consensus.common.bft.BftProcessor.run(BftProcessor.java:60)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)