Skip to content

Commit 8e66290

Browse files
matthew1001amsmota
authored andcommitted
Ensure empty withdrawal lists are set in BFT blocks when the protocol spec is shanghai or higher (besu-eth#6765)
* Ensure empty withdrawal lists are set in BFT blocks when the protocol schedule is shanghai or higher Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * Unit tests missing mock Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * Unit tests missing mock Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * Refactor and reduce code duplication Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> --------- Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> Signed-off-by: Matt Whitehead <matthew.whitehead@kaleido.io> Signed-off-by: amsmota <antonio.mota@citi.com>
1 parent 3922b75 commit 8e66290

File tree

11 files changed

+250
-12
lines changed

11 files changed

+250
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
- Make block transaction selection max time aware of PoA transitions [#6676](https://github.com/hyperledger/besu/pull/6676)
3434
- Don't enable the BFT mining coordinator when running sub commands such as `blocks export` [#6675](https://github.com/hyperledger/besu/pull/6675)
3535
- In JSON-RPC return optional `v` fields for type 1 and type 2 transactions [#6762](https://github.com/hyperledger/besu/pull/6762)
36+
- Fix Shanghai/QBFT block import bug when syncing new nodes [#6765](https://github.com/hyperledger/besu/pull/6765)
3637

3738
### Download Links
3839

consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BaseBftProtocolScheduleBuilder.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
3131
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
3232
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder;
33-
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsValidator;
3433
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
3534
import org.hyperledger.besu.evm.internal.EvmConfiguration;
3635

@@ -125,9 +124,6 @@ private ProtocolSpecBuilder applyBftChanges(
125124
.skipZeroBlockRewards(true)
126125
.blockHeaderFunctions(BftBlockHeaderFunctions.forOnchainBlock(bftExtraDataCodec))
127126
.blockReward(Wei.of(configOptions.getBlockRewardWei()))
128-
.withdrawalsValidator(
129-
new WithdrawalsValidator
130-
.ProhibitedWithdrawals()) // QBFT/IBFT doesn't support withdrawals
131127
.miningBeneficiaryCalculator(
132128
header -> configOptions.getMiningBeneficiary().orElseGet(header::getCoinbase));
133129
}

consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreator.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.hyperledger.besu.consensus.common.bft.BftBlockHeaderFunctions;
2020
import org.hyperledger.besu.consensus.common.bft.BftExtraDataCodec;
2121
import org.hyperledger.besu.consensus.common.bft.BftHelpers;
22+
import org.hyperledger.besu.consensus.common.bft.BftProtocolSchedule;
2223
import org.hyperledger.besu.datatypes.Address;
2324
import org.hyperledger.besu.ethereum.ProtocolContext;
2425
import org.hyperledger.besu.ethereum.blockcreation.AbstractBlockCreator;
@@ -29,7 +30,10 @@
2930
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
3031
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
3132
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
33+
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
34+
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsValidator;
3235

36+
import java.util.Collections;
3337
import java.util.Optional;
3438

3539
/** The Bft block creator. */
@@ -77,12 +81,31 @@ public BftBlockCreator(
7781
this.bftExtraDataCodec = bftExtraDataCodec;
7882
}
7983

84+
@Override
85+
public BlockCreationResult createBlock(final long timestamp) {
86+
ProtocolSpec protocolSpec =
87+
((BftProtocolSchedule) protocolSchedule)
88+
.getByBlockNumberOrTimestamp(parentHeader.getNumber() + 1, timestamp);
89+
90+
if (protocolSpec.getWithdrawalsValidator() instanceof WithdrawalsValidator.AllowedWithdrawals) {
91+
return createEmptyWithdrawalsBlock(timestamp);
92+
} else {
93+
return createBlock(Optional.empty(), Optional.empty(), timestamp);
94+
}
95+
}
96+
8097
private static MiningBeneficiaryCalculator miningBeneficiaryCalculator(
8198
final Address localAddress, final ForksSchedule<? extends BftConfigOptions> forksSchedule) {
8299
return blockNum ->
83100
forksSchedule.getFork(blockNum).getValue().getMiningBeneficiary().orElse(localAddress);
84101
}
85102

103+
@Override
104+
public BlockCreationResult createEmptyWithdrawalsBlock(final long timestamp) {
105+
return createBlock(
106+
Optional.empty(), Optional.empty(), Optional.of(Collections.emptyList()), timestamp);
107+
}
108+
86109
@Override
87110
protected BlockHeader createFinalBlockHeader(final SealableBlockHeader sealableBlockHeader) {
88111
final BlockHeaderBuilder builder =

consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,125 @@ public BlockHeaderValidator.Builder createBlockHeaderRuleset(
213213
new BftBlockHashing(bftExtraDataEncoder)
214214
.calculateDataHashForCommittedSeal(header, extraData));
215215
}
216+
217+
@Test
218+
public void testBlockCreationResultsInEmptyWithdrawalsListShanghaiOnwards() {
219+
// Construct a parent block.
220+
final BlockHeaderTestFixture blockHeaderBuilder = new BlockHeaderTestFixture();
221+
blockHeaderBuilder.gasLimit(5000); // required to pass validation rule checks.
222+
final BlockHeader parentHeader = blockHeaderBuilder.buildHeader();
223+
final Optional<BlockHeader> optionalHeader = Optional.of(parentHeader);
224+
225+
// Construct a blockchain and world state
226+
final MutableBlockchain blockchain = mock(MutableBlockchain.class);
227+
when(blockchain.getChainHeadHash()).thenReturn(parentHeader.getHash());
228+
when(blockchain.getBlockHeader(any())).thenReturn(optionalHeader);
229+
final BlockHeader blockHeader = mock(BlockHeader.class);
230+
when(blockHeader.getBaseFee()).thenReturn(Optional.empty());
231+
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
232+
233+
final List<Address> initialValidatorList = Lists.newArrayList();
234+
for (int i = 0; i < 4; i++) {
235+
initialValidatorList.add(AddressHelpers.ofValue(i));
236+
}
237+
238+
final IbftExtraDataCodec bftExtraDataEncoder = new IbftExtraDataCodec();
239+
240+
final BaseBftProtocolScheduleBuilder bftProtocolSchedule =
241+
new BaseBftProtocolScheduleBuilder() {
242+
@Override
243+
public BlockHeaderValidator.Builder createBlockHeaderRuleset(
244+
final BftConfigOptions config, final FeeMarket feeMarket) {
245+
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
246+
5, Optional.empty());
247+
}
248+
};
249+
final GenesisConfigOptions configOptions =
250+
GenesisConfigFile.fromConfig("{\"config\": {\"shanghaiTime\":0}}").getConfigOptions();
251+
final ForksSchedule<BftConfigOptions> forksSchedule =
252+
new ForksSchedule<>(List.of(new ForkSpec<>(0, configOptions.getBftConfigOptions())));
253+
final ProtocolSchedule protocolSchedule =
254+
bftProtocolSchedule.createProtocolSchedule(
255+
configOptions,
256+
forksSchedule,
257+
PrivacyParameters.DEFAULT,
258+
false,
259+
bftExtraDataEncoder,
260+
EvmConfiguration.DEFAULT,
261+
MiningParameters.MINING_DISABLED,
262+
new BadBlockManager());
263+
final ProtocolContext protContext =
264+
new ProtocolContext(
265+
blockchain,
266+
createInMemoryWorldStateArchive(),
267+
setupContextWithBftExtraDataEncoder(initialValidatorList, bftExtraDataEncoder),
268+
new BadBlockManager());
269+
270+
final TransactionPoolConfiguration poolConf =
271+
ImmutableTransactionPoolConfiguration.builder().txPoolMaxSize(1).build();
272+
273+
final GasPricePendingTransactionsSorter pendingTransactions =
274+
new GasPricePendingTransactionsSorter(
275+
poolConf,
276+
TestClock.system(ZoneId.systemDefault()),
277+
metricsSystem,
278+
blockchain::getChainHeadHeader);
279+
280+
final EthContext ethContext = mock(EthContext.class, RETURNS_DEEP_STUBS);
281+
when(ethContext.getEthPeers().subscribeConnect(any())).thenReturn(1L);
282+
283+
final TransactionPool transactionPool =
284+
new TransactionPool(
285+
() -> pendingTransactions,
286+
protocolSchedule,
287+
protContext,
288+
mock(TransactionBroadcaster.class),
289+
ethContext,
290+
new TransactionPoolMetrics(metricsSystem),
291+
poolConf);
292+
293+
transactionPool.setEnabled();
294+
295+
final MiningParameters miningParameters =
296+
ImmutableMiningParameters.builder()
297+
.mutableInitValues(
298+
MutableInitValues.builder()
299+
.extraData(
300+
bftExtraDataEncoder.encode(
301+
new BftExtraData(
302+
Bytes.wrap(new byte[32]),
303+
Collections.emptyList(),
304+
Optional.empty(),
305+
0,
306+
initialValidatorList)))
307+
.minTransactionGasPrice(Wei.ZERO)
308+
.coinbase(AddressHelpers.ofValue(1))
309+
.build())
310+
.build();
311+
312+
final BftBlockCreator blockCreator =
313+
new BftBlockCreator(
314+
miningParameters,
315+
forksSchedule,
316+
initialValidatorList.get(0),
317+
parent ->
318+
bftExtraDataEncoder.encode(
319+
new BftExtraData(
320+
Bytes.wrap(new byte[32]),
321+
Collections.emptyList(),
322+
Optional.empty(),
323+
0,
324+
initialValidatorList)),
325+
transactionPool,
326+
protContext,
327+
protocolSchedule,
328+
parentHeader,
329+
bftExtraDataEncoder,
330+
new DeterministicEthScheduler());
331+
332+
final Block block = blockCreator.createBlock(parentHeader.getTimestamp() + 1).getBlock();
333+
334+
// Test that a BFT block contains an empty withdrawals list (not an optional empty)
335+
assertThat(block.getBody().getWithdrawals().isPresent()).isTrue();
336+
}
216337
}

consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRoundTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.hyperledger.besu.ethereum.core.BlockImporter;
6060
import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
6161
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
62+
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsValidator;
6263
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
6364
import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException;
6465
import org.hyperledger.besu.util.Subscribers;
@@ -273,6 +274,9 @@ public void twoValidatorNetworkSendsPrepareOnProposalReceptionThenSendsCommitOnC
273274

274275
@Test
275276
public void localNodeProposesToNetworkOfTwoValidatorsImportsOnReceptionOfCommitFromPeer() {
277+
lenient()
278+
.when(protocolSpec.getWithdrawalsValidator())
279+
.thenReturn(new WithdrawalsValidator.AllowedWithdrawals());
276280
final RoundState roundState = new RoundState(roundIdentifier, 2, messageValidator);
277281
final IbftRound round =
278282
new IbftRound(

consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/blockcreation/PkiQbftBlockCreator.java

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.hyperledger.besu.consensus.common.bft.BftBlockHeaderFunctions;
2020
import org.hyperledger.besu.consensus.common.bft.BftExtraData;
2121
import org.hyperledger.besu.consensus.common.bft.BftExtraDataCodec;
22+
import org.hyperledger.besu.consensus.common.bft.BftProtocolSchedule;
2223
import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfiguration;
2324
import org.hyperledger.besu.consensus.qbft.pki.PkiQbftBlockHeaderFunctions;
2425
import org.hyperledger.besu.consensus.qbft.pki.PkiQbftExtraData;
@@ -29,6 +30,9 @@
2930
import org.hyperledger.besu.ethereum.core.BlockHeader;
3031
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
3132
import org.hyperledger.besu.ethereum.core.Transaction;
33+
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
34+
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
35+
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsValidator;
3236
import org.hyperledger.besu.pki.cms.CmsCreator;
3337

3438
import java.util.Collections;
@@ -48,24 +52,33 @@ public class PkiQbftBlockCreator implements BlockCreator {
4852
private final BlockCreator blockCreator;
4953
private final PkiQbftExtraDataCodec pkiQbftExtraDataCodec;
5054
private final CmsCreator cmsCreator;
55+
private final BlockHeader parentHeader;
56+
private final ProtocolSchedule protocolSchedule;
5157

5258
/**
5359
* Instantiates a new Pki qbft block creator.
5460
*
5561
* @param blockCreator the block creator
5662
* @param pkiBlockCreationConfiguration the pki block creation configuration
5763
* @param pkiQbftExtraDataCodec the pki qbft extra data codec
64+
* @param parentHeader the block header of the parent block
65+
* @param protocolSchedule the protocol schedule (the type of block can vary based on the current
66+
* protocol spec)
5867
*/
5968
public PkiQbftBlockCreator(
6069
final BlockCreator blockCreator,
6170
final PkiBlockCreationConfiguration pkiBlockCreationConfiguration,
62-
final BftExtraDataCodec pkiQbftExtraDataCodec) {
71+
final BftExtraDataCodec pkiQbftExtraDataCodec,
72+
final BlockHeader parentHeader,
73+
final ProtocolSchedule protocolSchedule) {
6374
this(
6475
blockCreator,
6576
new CmsCreator(
6677
pkiBlockCreationConfiguration.getKeyStore(),
6778
pkiBlockCreationConfiguration.getCertificateAlias()),
68-
pkiQbftExtraDataCodec);
79+
pkiQbftExtraDataCodec,
80+
parentHeader,
81+
protocolSchedule);
6982
}
7083

7184
/**
@@ -74,14 +87,21 @@ public PkiQbftBlockCreator(
7487
* @param blockCreator the block creator
7588
* @param cmsCreator the cms creator
7689
* @param bftExtraDataCodec the bft extra data codec
90+
* @param parentHeader the block header of the parent block
91+
* @param protocolSchedule the protocol schedule (the type of block can vary based on the current
92+
* protocol spec)
7793
*/
7894
@VisibleForTesting
7995
public PkiQbftBlockCreator(
8096
final BlockCreator blockCreator,
8197
final CmsCreator cmsCreator,
82-
final BftExtraDataCodec bftExtraDataCodec) {
98+
final BftExtraDataCodec bftExtraDataCodec,
99+
final BlockHeader parentHeader,
100+
final ProtocolSchedule protocolSchedule) {
83101
this.blockCreator = blockCreator;
84102
this.cmsCreator = cmsCreator;
103+
this.protocolSchedule = protocolSchedule;
104+
this.parentHeader = parentHeader;
85105

86106
checkArgument(
87107
bftExtraDataCodec instanceof PkiQbftExtraDataCodec,
@@ -91,7 +111,16 @@ public PkiQbftBlockCreator(
91111

92112
@Override
93113
public BlockCreationResult createBlock(final long timestamp) {
94-
final BlockCreationResult blockCreationResult = blockCreator.createBlock(timestamp);
114+
ProtocolSpec protocolSpec =
115+
((BftProtocolSchedule) protocolSchedule)
116+
.getByBlockNumberOrTimestamp(parentHeader.getNumber() + 1, timestamp);
117+
118+
final BlockCreationResult blockCreationResult;
119+
if (protocolSpec.getWithdrawalsValidator() instanceof WithdrawalsValidator.AllowedWithdrawals) {
120+
blockCreationResult = blockCreator.createEmptyWithdrawalsBlock(timestamp);
121+
} else {
122+
blockCreationResult = blockCreator.createBlock(timestamp);
123+
}
95124
return replaceCmsInBlock(blockCreationResult);
96125
}
97126

@@ -114,6 +143,13 @@ public BlockCreationResult createBlock(
114143
timestamp);
115144
}
116145

146+
@Override
147+
public BlockCreationResult createEmptyWithdrawalsBlock(final long timestamp) {
148+
final BlockCreationResult blockCreationResult =
149+
blockCreator.createEmptyWithdrawalsBlock(timestamp);
150+
return replaceCmsInBlock(blockCreationResult);
151+
}
152+
117153
private BlockCreationResult replaceCmsInBlock(final BlockCreationResult blockCreationResult) {
118154
final Block block = blockCreationResult.getBlock();
119155
final BlockHeader blockHeader = block.getHeader();

consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/blockcreation/QbftBlockCreatorFactory.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ public BlockCreator create(final BlockHeader parentHeader, final int round) {
7777
return blockCreator;
7878
} else {
7979
return new PkiQbftBlockCreator(
80-
blockCreator, qbftContext.getPkiBlockCreationConfiguration().get(), bftExtraDataCodec);
80+
blockCreator,
81+
qbftContext.getPkiBlockCreationConfiguration().get(),
82+
bftExtraDataCodec,
83+
parentHeader,
84+
protocolSchedule);
8185
}
8286
}
8387

0 commit comments

Comments
 (0)