Skip to content

Commit

Permalink
Refactored version of QBFT/shanghai
Browse files Browse the repository at this point in the history
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
  • Loading branch information
matthew1001 committed Jan 11, 2024
1 parent dbaa34d commit 7cd7178
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsValidator;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.internal.EvmConfiguration;

Expand Down Expand Up @@ -116,6 +117,9 @@ private ProtocolSpecBuilder applyBftChanges(
.skipZeroBlockRewards(true)
.blockHeaderFunctions(BftBlockHeaderFunctions.forOnchainBlock(bftExtraDataCodec))
.blockReward(Wei.of(configOptions.getBlockRewardWei()))
.withdrawalsValidator(
new WithdrawalsValidator
.ProhibitedWithdrawals()) // QBFT/IBFT doesn't support withdrawals
.miningBeneficiaryCalculator(
header -> configOptions.getMiningBeneficiary().orElseGet(header::getCoinbase));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,35 @@ public ProtocolSpec getByBlockNumber(final long number) {
return null;
}

/**
* Look up ProtocolSpec by block number and timestamp
*
* @param number block number
* @param timestamp block timestamp
* @return the protocol spec for that block number and timestamp
*/
public ProtocolSpec getByBlockNumberAndTimestamp(final long number, final long timestamp) {
checkArgument(number >= 0, "number must be non-negative");
checkArgument(
!protocolSpecs.isEmpty(), "At least 1 milestone must be provided to the protocol schedule");
checkArgument(
protocolSpecs.last().fork().milestone() == 0,
"There must be a milestone starting from block 0");
// protocolSpecs is sorted in descending block order, so the first one we find that's lower than
// the requested level will be the most appropriate spec
ProtocolSpec theSpec = null;
for (final ScheduledProtocolSpec s : protocolSpecs) {
if (number >= s.fork().milestone()) {
theSpec = s.spec();
break;
} else if (timestamp >= s.fork().milestone()) {
theSpec = s.spec();
break;
}
}
return theSpec;
}

/**
* return the ordered list of scheduled protocol specs
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public QbftBlockHeightManager(
new RoundState(
roundIdentifier,
finalState.getQuorum(),
messageValidatorFactory.createMessageValidator(roundIdentifier, parentHeader));
messageValidatorFactory.createMessageValidator(roundIdentifier, parentHeader, 0L));

final long nextBlockHeight = parentHeader.getNumber() + 1;
final ConsensusRoundIdentifier roundIdentifier =
Expand Down Expand Up @@ -280,15 +280,18 @@ public void handleRoundChangePayload(final RoundChange message) {

private void startNewRound(final int roundNumber) {
LOG.debug("Starting new round {}", roundNumber);
final long headerTimeStampSeconds = Math.round(clock.millis() / 1000D);
// validate the current round
if (futureRoundStateBuffer.containsKey(roundNumber)) {
currentRound =
Optional.of(
roundFactory.createNewRoundWithState(
parentHeader, futureRoundStateBuffer.get(roundNumber)));
parentHeader, futureRoundStateBuffer.get(roundNumber), headerTimeStampSeconds));
futureRoundStateBuffer.keySet().removeIf(k -> k <= roundNumber);
} else {
currentRound = Optional.of(roundFactory.createNewRound(parentHeader, roundNumber));
currentRound =
Optional.of(
roundFactory.createNewRound(parentHeader, roundNumber, headerTimeStampSeconds));
}
// discard roundChange messages from the current and previous rounds
roundChangeManager.discardRoundsPriorTo(currentRound.get().getRoundIdentifier());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.hyperledger.besu.ethereum.blockcreation.BlockCreator;
import org.hyperledger.besu.ethereum.chain.MinedBlockObserver;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockImporter;
import org.hyperledger.besu.util.Subscribers;

/** The Qbft round factory. */
Expand Down Expand Up @@ -74,9 +75,12 @@ public QbftRoundFactory(
*
* @param parentHeader the parent header
* @param round the round
* @param blockTimestamp the timestamp of the new round, and hence the timestamp of the protocol
* schedule to use to create the block
* @return the qbft round
*/
public QbftRound createNewRound(final BlockHeader parentHeader, final int round) {
public QbftRound createNewRound(
final BlockHeader parentHeader, final int round, final long blockTimestamp) {
long nextBlockHeight = parentHeader.getNumber() + 1;
final ConsensusRoundIdentifier roundIdentifier =
new ConsensusRoundIdentifier(nextBlockHeight, round);
Expand All @@ -85,32 +89,40 @@ public QbftRound createNewRound(final BlockHeader parentHeader, final int round)
new RoundState(
roundIdentifier,
finalState.getQuorum(),
messageValidatorFactory.createMessageValidator(roundIdentifier, parentHeader));
messageValidatorFactory.createMessageValidator(
roundIdentifier, parentHeader, blockTimestamp));

return createNewRoundWithState(parentHeader, roundState);
return createNewRoundWithState(parentHeader, roundState, blockTimestamp);
}

/**
* Create new Qbft round with state.
*
* @param parentHeader the parent header
* @param roundState the round state
* @param blockTimestamp the timestamp of the new round, and hence the timestamp of the protocol
* schedule to use to create the block
* @return the qbft round
*/
public QbftRound createNewRoundWithState(
final BlockHeader parentHeader, final RoundState roundState) {
final BlockHeader parentHeader, final RoundState roundState, final long blockTimestamp) {
final ConsensusRoundIdentifier roundIdentifier = roundState.getRoundIdentifier();
final BlockCreator blockCreator = blockCreatorFactory.create(parentHeader, 0);

// TODO(tmm): Why is this created everytime?!
final QbftMessageTransmitter messageTransmitter =
new QbftMessageTransmitter(messageFactory, finalState.getValidatorMulticaster());

final BlockImporter theImporter =
protocolSchedule
.getByBlockNumberAndTimestamp(roundIdentifier.getSequenceNumber(), blockTimestamp)
.getBlockImporter();

return new QbftRound(
roundState,
blockCreator,
protocolContext,
protocolSchedule.getByBlockNumber(roundIdentifier.getSequenceNumber()).getBlockImporter(),
theImporter,
minedBlockObservers,
finalState.getNodeKey(),
messageFactory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ public boolean validateProposalMessage(final Proposal msg) {
new ConsensusRoundIdentifier(chainHeight, msg.getRoundIdentifier().getRoundNumber());

final MessageValidator messageValidator =
messageValidatorFactory.createMessageValidator(roundIdentifier, parentHeader);
messageValidatorFactory.createMessageValidator(
roundIdentifier, parentHeader, msg.getBlock().getHeader().getTimestamp());

return messageValidator.validateProposal(msg);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,19 @@ public RoundChangeMessageValidator createRoundChangeMessageValidator(
*
* @param roundIdentifier the round identifier
* @param parentHeader the parent header
* @param headerTimestamp the timestamp of the header
* @return the message validator
*/
public MessageValidator createMessageValidator(
final ConsensusRoundIdentifier roundIdentifier, final BlockHeader parentHeader) {
final ConsensusRoundIdentifier roundIdentifier,
final BlockHeader parentHeader,
final long headerTimestamp) {

final Collection<Address> validatorsForHeight = getValidatorsAfterBlock(parentHeader);
final BlockValidator blockValidator =
protocolSchedule.getByBlockNumber(roundIdentifier.getSequenceNumber()).getBlockValidator();
protocolSchedule
.getByBlockNumberAndTimestamp(roundIdentifier.getSequenceNumber(), headerTimestamp)
.getBlockValidator();

final ProposalValidator proposalValidator =
new ProposalValidator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ public void setup() {
when(futureRoundProposalMessageValidator.validateProposalMessage(any())).thenReturn(true);
when(messageValidatorFactory.createFutureRoundProposalMessageValidator(anyLong(), any()))
.thenReturn(futureRoundProposalMessageValidator);
when(messageValidatorFactory.createMessageValidator(any(), any())).thenReturn(messageValidator);
when(messageValidatorFactory.createMessageValidator(any(), any(), anyLong()))
.thenReturn(messageValidator);
when(blockImporter.importBlock(any(), any(), any())).thenReturn(new BlockImportResult(false));

protocolContext =
Expand All @@ -164,7 +165,7 @@ QbftContext.class, validators, new QbftExtraDataCodec()),
Optional.empty());

// Ensure the created QbftRound has the valid ConsensusRoundIdentifier;
when(roundFactory.createNewRound(any(), anyInt()))
when(roundFactory.createNewRound(any(), anyInt(), anyLong()))
.thenAnswer(
invocation -> {
final int round = invocation.getArgument(1);
Expand All @@ -183,7 +184,7 @@ QbftContext.class, validators, new QbftExtraDataCodec()),
bftExtraDataCodec);
});

when(roundFactory.createNewRoundWithState(any(), any()))
when(roundFactory.createNewRoundWithState(any(), any(), anyLong()))
.thenAnswer(
invocation -> {
final RoundState providedRoundState = invocation.getArgument(1);
Expand Down Expand Up @@ -323,13 +324,13 @@ public void onRoundChangeReceptionRoundChangeManagerIsInvokedAndNewRoundStarted(
messageValidatorFactory,
messageFactory);
manager.handleBlockTimerExpiry(roundIdentifier);
verify(roundFactory).createNewRound(any(), eq(0));
verify(roundFactory).createNewRound(any(), eq(0), anyLong());

manager.handleRoundChangePayload(roundChange);

verify(roundChangeManager, times(1)).appendRoundChangeMessage(roundChange);
verify(roundFactory, times(1))
.createNewRound(any(), eq(futureRoundIdentifier.getRoundNumber()));
.createNewRound(any(), eq(futureRoundIdentifier.getRoundNumber()), anyLong());
}

@Test
Expand All @@ -344,10 +345,10 @@ public void onRoundTimerExpiryANewRoundIsCreatedWithAnIncrementedRoundNumber() {
messageValidatorFactory,
messageFactory);
manager.handleBlockTimerExpiry(roundIdentifier);
verify(roundFactory).createNewRound(any(), eq(0));
verify(roundFactory).createNewRound(any(), eq(0), anyLong());

manager.roundExpired(new RoundExpiry(roundIdentifier));
verify(roundFactory).createNewRound(any(), eq(1));
verify(roundFactory).createNewRound(any(), eq(1), anyLong());
}

@Test
Expand Down Expand Up @@ -542,6 +543,6 @@ public void illegalFutureRoundProposalDoesNotTriggerNewRound() {
reset(roundFactory); // Discard the existing createNewRound invocation.

manager.handleProposalPayload(futureRoundProposal);
verify(roundFactory, never()).createNewRound(any(), anyInt());
verify(roundFactory, never()).createNewRound(any(), anyInt(), anyLong());
}
}

0 comments on commit 7cd7178

Please sign in to comment.