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 empty withdrawal lists are set in BFT blocks when the protocol spec is shanghai or higher #6765

Merged
merged 5 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -27,6 +27,7 @@
- Fix txpool dump/restore race condition [#6665](https://github.com/hyperledger/besu/pull/6665)
- Make block transaction selection max time aware of PoA transitions [#6676](https://github.com/hyperledger/besu/pull/6676)
- Don't enable the BFT mining coordinator when running sub commands such as `blocks export` [#6675](https://github.com/hyperledger/besu/pull/6675)
- Fix Shanghai/QBFT block import bug when syncing new nodes [#6765](https://github.com/hyperledger/besu/pull/6765)

### Download Links

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
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 @@ -125,9 +124,6 @@ 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 @@ -30,6 +30,7 @@
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;

import java.util.Collections;
import java.util.Optional;

/** The Bft block creator. */
Expand Down Expand Up @@ -83,6 +84,12 @@ private static MiningBeneficiaryCalculator miningBeneficiaryCalculator(
forksSchedule.getFork(blockNum).getValue().getMiningBeneficiary().orElse(localAddress);
}

@Override
public BlockCreationResult createEmptyWithdrawalsBlock(final long timestamp) {
return createBlock(
Optional.empty(), Optional.empty(), Optional.of(Collections.emptyList()), timestamp);
}

@Override
protected BlockHeader createFinalBlockHeader(final SealableBlockHeader sealableBlockHeader) {
final BlockHeaderBuilder builder =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.hyperledger.besu.consensus.common.bft.BftExtraData;
import org.hyperledger.besu.consensus.common.bft.BftExtraDataCodec;
import org.hyperledger.besu.consensus.common.bft.BftHelpers;
import org.hyperledger.besu.consensus.common.bft.BftProtocolSchedule;
import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier;
import org.hyperledger.besu.consensus.common.bft.RoundTimer;
import org.hyperledger.besu.consensus.ibft.messagewrappers.Commit;
Expand All @@ -41,6 +42,8 @@
import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsValidator;
import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException;
import org.hyperledger.besu.util.Subscribers;

Expand Down Expand Up @@ -118,7 +121,20 @@ public ConsensusRoundIdentifier getRoundIdentifier() {
* @param headerTimeStampSeconds the header time stamp seconds
*/
public void createAndSendProposalMessage(final long headerTimeStampSeconds) {
final Block block = blockCreator.createBlock(headerTimeStampSeconds).getBlock();

// Determine if we're at a protocol spec that requires withdrawals (even if empty) to be present
ProtocolSpec protocolSpec =
Copy link
Contributor

Choose a reason for hiding this comment

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

This is duplicated several times. It feels like we can do this in BftBlockCreator by overriding createBlock, checking the withdrawal validator, and switching on that. If the sequence number is truly needed then making a new createBlock method with both args. May need to also do it for PkuQbftBlockCreator since there is no shared parent classes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good thought. I've done some refactoring as BftBlockCreator already has access to the parent header required to get the next block number. So it was a little easier than I thought. Now QbftRound and IbftRound can just continue to call createBlock(...) as they did before, and BftBlockCreator.createBlock(...) can decide which type of block to actually create. There's a little repetition in PkiQbftBlockCreator like you say, but it's definitely tidier now.

((BftProtocolSchedule) protocolSchedule)
.getByBlockNumberOrTimestamp(
roundState.getRoundIdentifier().getSequenceNumber(), headerTimeStampSeconds);

final Block block;
if (protocolSpec.getWithdrawalsValidator() instanceof WithdrawalsValidator.AllowedWithdrawals) {
block = blockCreator.createEmptyWithdrawalsBlock(headerTimeStampSeconds).getBlock();
} else {
block = blockCreator.createBlock(headerTimeStampSeconds).getBlock();
}

final BftExtraData extraData = bftExtraDataCodec.decode(block.getHeader());
LOG.debug("Creating proposed block. round={}", roundState.getRoundIdentifier());
LOG.trace(
Expand All @@ -141,7 +157,20 @@ public void startRoundWith(
final Block blockToPublish;
if (!bestBlockFromRoundChange.isPresent()) {
LOG.debug("Sending proposal with new block. round={}", roundState.getRoundIdentifier());
blockToPublish = blockCreator.createBlock(headerTimestamp).getBlock();

// Determine if we're at a protocol spec that requires withdrawals (even if empty) to be
// present
ProtocolSpec protocolSpec =
((BftProtocolSchedule) protocolSchedule)
.getByBlockNumberOrTimestamp(
roundState.getRoundIdentifier().getSequenceNumber(), headerTimestamp);

if (protocolSpec.getWithdrawalsValidator()
instanceof WithdrawalsValidator.AllowedWithdrawals) {
blockToPublish = blockCreator.createEmptyWithdrawalsBlock(headerTimestamp).getBlock();
} else {
blockToPublish = blockCreator.createBlock(headerTimestamp).getBlock();
}
} else {
LOG.debug(
"Sending proposal from PreparedCertificate. round={}", roundState.getRoundIdentifier());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ public void setup() {
.when(blockImporter.importBlock(any(), any(), any()))
.thenReturn(new BlockImportResult(BlockImportResult.BlockImportStatus.IMPORTED));

lenient()
.when(protocolSchedule.getByBlockNumberOrTimestamp(anyLong(), anyLong()))
.thenReturn(protocolSpec);

subscribers.subscribe(minedBlockObserver);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ public BlockCreationResult createBlock(
timestamp);
}

@Override
public BlockCreationResult createEmptyWithdrawalsBlock(final long timestamp) {
final BlockCreationResult blockCreationResult =
blockCreator.createEmptyWithdrawalsBlock(timestamp);
return replaceCmsInBlock(blockCreationResult);
}

private BlockCreationResult replaceCmsInBlock(final BlockCreationResult blockCreationResult) {
final Block block = blockCreationResult.getBlock();
final BlockHeader blockHeader = block.getHeader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.hyperledger.besu.consensus.common.bft.BftExtraData;
import org.hyperledger.besu.consensus.common.bft.BftExtraDataCodec;
import org.hyperledger.besu.consensus.common.bft.BftHelpers;
import org.hyperledger.besu.consensus.common.bft.BftProtocolSchedule;
import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier;
import org.hyperledger.besu.consensus.common.bft.RoundTimer;
import org.hyperledger.besu.consensus.common.bft.payload.SignedData;
Expand All @@ -45,6 +46,8 @@
import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsValidator;
import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException;
import org.hyperledger.besu.util.Subscribers;

Expand Down Expand Up @@ -129,8 +132,19 @@ public ConsensusRoundIdentifier getRoundIdentifier() {
*/
public void createAndSendProposalMessage(final long headerTimeStampSeconds) {
LOG.debug("Creating proposed block. round={}", roundState.getRoundIdentifier());
final Block block = blockCreator.createBlock(headerTimeStampSeconds).getBlock();

// Determine if we're at a protocol spec that requires withdrawals (even if empty) to be present
ProtocolSpec protocolSpec =
((BftProtocolSchedule) protocolSchedule)
.getByBlockNumberOrTimestamp(
roundState.getRoundIdentifier().getSequenceNumber(), headerTimeStampSeconds);

final Block block;
if (protocolSpec.getWithdrawalsValidator() instanceof WithdrawalsValidator.AllowedWithdrawals) {
block = blockCreator.createEmptyWithdrawalsBlock(headerTimeStampSeconds).getBlock();
} else {
block = blockCreator.createBlock(headerTimeStampSeconds).getBlock();
}
LOG.trace("Creating proposed block blockHeader={}", block.getHeader());
updateStateWithProposalAndTransmit(block, emptyList(), emptyList());
}
Expand All @@ -148,8 +162,21 @@ public void startRoundWith(

final Block blockToPublish;
if (bestPreparedCertificate.isEmpty()) {

// Determine if we're at a protocol spec that requires withdrawals (even if empty) to be
// present
ProtocolSpec protocolSpec =
((BftProtocolSchedule) protocolSchedule)
.getByBlockNumberOrTimestamp(
roundState.getRoundIdentifier().getSequenceNumber(), headerTimestamp);

if (protocolSpec.getWithdrawalsValidator()
instanceof WithdrawalsValidator.AllowedWithdrawals) {
blockToPublish = blockCreator.createEmptyWithdrawalsBlock(headerTimestamp).getBlock();
} else {
blockToPublish = blockCreator.createBlock(headerTimestamp).getBlock();
}
LOG.debug("Sending proposal with new block. round={}", roundState.getRoundIdentifier());
blockToPublish = blockCreator.createBlock(headerTimestamp).getBlock();
} else {
LOG.debug(
"Sending proposal from PreparedCertificate. round={}", roundState.getRoundIdentifier());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ public class ProposalTest {
private static final Block BLOCK =
new Block(
new BlockHeaderTestFixture().extraData(bftExtraDataCodec.encode(extraData)).buildHeader(),
new BlockBody(Collections.emptyList(), Collections.emptyList()));
new BlockBody(
Collections.emptyList(),
Collections.emptyList(),
Optional.of(Collections.emptyList()),
Optional.empty()));

@Test
public void canRoundTripProposalMessage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ QbftContext.class, emptyList(), new QbftExtraDataCodec()),
when(blockImporter.importBlock(any(), any(), any()))
.thenReturn(new BlockImportResult(BlockImportResult.BlockImportStatus.IMPORTED));

when(protocolSchedule.getByBlockNumberOrTimestamp(anyLong(), anyLong()))
.thenReturn(protocolSpec);

subscribers.subscribe(minedBlockObserver);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,26 @@ public BlockCreationResult createBlock(
true);
}

@Override
public BlockCreationResult createEmptyWithdrawalsBlock(final long timestamp) {
throw new UnsupportedOperationException("Only used by BFT block creators");
}

public BlockCreationResult createBlock(
final Optional<List<Transaction>> maybeTransactions,
final Optional<List<BlockHeader>> maybeOmmers,
final Optional<List<Withdrawal>> maybeWithdrawals,
final long timestamp) {
return createBlock(
maybeTransactions,
maybeOmmers,
maybeWithdrawals,
Optional.empty(),
Optional.empty(),
timestamp,
true);
}

protected BlockCreationResult createBlock(
final Optional<List<Transaction>> maybeTransactions,
final Optional<List<BlockHeader>> maybeOmmers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public TransactionSelectionResults getTransactionSelectionResults() {

BlockCreationResult createBlock(final long timestamp);

BlockCreationResult createEmptyWithdrawalsBlock(final long timestamp);

BlockCreationResult createBlock(
final List<Transaction> transactions, final List<BlockHeader> ommers, final long timestamp);

Expand Down
Loading