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

QBFT + shanghai support #6353

Merged
merged 22 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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 @@ -28,6 +28,7 @@
- Introduce caching mechanism to optimize Keccak hash calculations for account storage slots during block processing [#6452](https://github.com/hyperledger/besu/pull/6452)
- Added configuration options for `pragueTime` to genesis file for Prague fork development [#6473](https://github.com/hyperledger/besu/pull/6473)
- Moving trielog storage to RocksDB's blobdb to improve write amplications [#6289](https://github.com/hyperledger/besu/pull/6289)
- Support for `shanghaiTime` fork and Shanghai EVM smart contracts in QBFT/IBFT chains [#6353](https://github.com/hyperledger/besu/pull/6353)

### Bug fixes
- Fix the way an advertised host configured with `--p2p-host` is treated when communicating with the originator of a PING packet [#6225](https://github.com/hyperledger/besu/pull/6225)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,36 @@ public void shouldMineOnSingleNodeWithFreeGas_London(
cluster.verify(receiver.balanceEquals(3));
}

@ParameterizedTest(name = "{index}: {0}")
@MethodSource("factoryFunctions")
public void shouldMineOnSingleNodeWithFreeGas_Shanghai(
final String testName, final BftAcceptanceTestParameterization nodeFactory) throws Exception {
setUp(testName, nodeFactory);
final BesuNode minerNode = nodeFactory.createNode(besu, "miner1");
updateGenesisConfigToShanghai(minerNode, true);

cluster.start(minerNode);

cluster.verify(blockchain.reachesHeight(minerNode, 1));

final Account sender = accounts.createAccount("account1");
final Account receiver = accounts.createAccount("account2");

minerNode.execute(accountTransactions.createTransfer(sender, 50, Amount.ZERO));
cluster.verify(sender.balanceEquals(50));

minerNode.execute(accountTransactions.create1559Transfer(sender, 50, 4, Amount.ZERO));
cluster.verify(sender.balanceEquals(100));

minerNode.execute(
accountTransactions.createIncrementalTransfers(sender, receiver, 1, Amount.ZERO));
cluster.verify(receiver.balanceEquals(1));

minerNode.execute(
accountTransactions.create1559IncrementalTransfers(sender, receiver, 2, 4, Amount.ZERO));
cluster.verify(receiver.balanceEquals(3));
}

@ParameterizedTest(name = "{index}: {0}")
@MethodSource("factoryFunctions")
public void shouldMineOnMultipleNodes(
Expand Down Expand Up @@ -245,4 +275,16 @@ private static void updateGenesisConfigToLondon(
config.put("zeroBaseFee", zeroBaseFeeEnabled);
minerNode.setGenesisConfig(genesisConfigNode.toString());
}

private static void updateGenesisConfigToShanghai(
final BesuNode minerNode, final boolean zeroBaseFeeEnabled) {
final Optional<String> genesisConfig =
minerNode.getGenesisConfigProvider().create(List.of(minerNode));
final ObjectNode genesisConfigNode = JsonUtil.objectNodeFromString(genesisConfig.orElseThrow());
final ObjectNode config = (ObjectNode) genesisConfigNode.get("config");
config.remove("berlinBlock");
config.put("shanghaiTime", 100);
config.put("zeroBaseFee", zeroBaseFeeEnabled);
minerNode.setGenesisConfig(genesisConfigNode.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,22 @@ public BftProtocolSchedule create(
protocolSchedule.getScheduledProtocolSpecs().stream()
.filter(protocolSpecMatchesConsensusBlockRange(spec.getBlock(), endBlock))
.forEach(
s ->
combinedProtocolSchedule.putBlockNumberMilestone(s.fork().milestone(), s.spec()));
s -> {
if (s instanceof ScheduledProtocolSpec.TimestampProtocolSpec) {
combinedProtocolSchedule.putTimestampMilestone(s.fork().milestone(), s.spec());
} else if (s instanceof ScheduledProtocolSpec.BlockNumberProtocolSpec) {
combinedProtocolSchedule.putBlockNumberMilestone(s.fork().milestone(), s.spec());
} else {
throw new IllegalStateException(
"Unexpected milestone: " + s + " for milestone: " + s.fork().milestone());
}
});

// When moving to a new consensus mechanism we want to use the last milestone but created by
// our consensus mechanism's BesuControllerBuilder so any additional rules are applied
if (spec.getBlock() > 0) {
combinedProtocolSchedule.putBlockNumberMilestone(
spec.getBlock(), protocolSchedule.getByBlockNumber(spec.getBlock()));
spec.getBlock(), protocolSchedule.getByBlockNumberOrTimestamp(spec.getBlock(), 0L));
}
}
return combinedProtocolSchedule;
Expand Down
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 @@ -39,12 +39,13 @@ public BftProtocolSchedule(final DefaultProtocolSchedule protocolSchedule) {
}

/**
* Look up ProtocolSpec by block number
* Look up ProtocolSpec by block number or timestamp
*
* @param number block number
* @return the protocol spec for that block number
* @param timestamp block timestamp
* @return the protocol spec for that block number or timestamp
*/
public ProtocolSpec getByBlockNumber(final long number) {
public ProtocolSpec getByBlockNumberOrTimestamp(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");
Expand All @@ -53,12 +54,19 @@ public ProtocolSpec getByBlockNumber(final long number) {
"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()) {
return s.spec();
if ((s instanceof ScheduledProtocolSpec.BlockNumberProtocolSpec)
&& (number >= s.fork().milestone())) {
theSpec = s.spec();
break;
} else if ((s instanceof ScheduledProtocolSpec.TimestampProtocolSpec)
&& (timestamp >= s.fork().milestone())) {
theSpec = s.spec();
break;
}
}
return null;
return theSpec;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,20 @@ public void createsCombinedProtocolScheduleWithMilestonesFromSingleProtocolSched
final BftProtocolSchedule combinedProtocolSchedule =
combinedProtocolScheduleFactory.create(consensusSchedule, Optional.of(BigInteger.TEN));

assertThat(combinedProtocolSchedule.getByBlockNumber(0L).getName()).isEqualTo("Frontier");
assertThat(combinedProtocolSchedule.getByBlockNumber(0L))
.isSameAs(protocolSchedule.getByBlockNumber(0L));
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(0L, 0L).getName())
.isEqualTo("Frontier");
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(0L, 0L))
.isSameAs(protocolSchedule.getByBlockNumberOrTimestamp(0L, 0L));

assertThat(combinedProtocolSchedule.getByBlockNumber(5L).getName()).isEqualTo("Homestead");
assertThat(combinedProtocolSchedule.getByBlockNumber(5L))
.isSameAs(protocolSchedule.getByBlockNumber(5L));
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(5L, 0L).getName())
.isEqualTo("Homestead");
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(5L, 0L))
.isSameAs(protocolSchedule.getByBlockNumberOrTimestamp(5L, 0L));

assertThat(combinedProtocolSchedule.getByBlockNumber(10L).getName())
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(10L, 0L).getName())
.isEqualTo("Constantinople");
assertThat(combinedProtocolSchedule.getByBlockNumber(10L))
.isSameAs(protocolSchedule.getByBlockNumber(10L));
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(10L, 0L))
.isSameAs(protocolSchedule.getByBlockNumberOrTimestamp(10L, 0L));

assertThat(
new MilestoneStreamingProtocolSchedule(combinedProtocolSchedule)
Expand All @@ -88,63 +90,78 @@ public void createsCombinedProtocolScheduleWithMilestonesFromMultipleSchedules()
genesisConfigOptions.byzantiumBlock(105L);
genesisConfigOptions.berlinBlock(110L);
genesisConfigOptions.londonBlock(220L);
genesisConfigOptions.shanghaiTime(1000000050L);
genesisConfigOptions.chainId(BigInteger.TEN);

final BftProtocolSchedule protocolSchedule1 = createProtocolSchedule(genesisConfigOptions);
final BftProtocolSchedule protocolSchedule2 = createProtocolSchedule(genesisConfigOptions);
final BftProtocolSchedule protocolSchedule3 = createProtocolSchedule(genesisConfigOptions);
final BftProtocolSchedule protocolSchedule4 = createProtocolSchedule(genesisConfigOptions);

final NavigableSet<ForkSpec<ProtocolSchedule>> consensusSchedule =
new TreeSet<>(ForkSpec.COMPARATOR);
consensusSchedule.add(new ForkSpec<>(0, protocolSchedule1));
consensusSchedule.add(new ForkSpec<>(100L, protocolSchedule2));
consensusSchedule.add(new ForkSpec<>(200L, protocolSchedule3));
consensusSchedule.add(new ForkSpec<>(1000000000L, protocolSchedule4));

final BftProtocolSchedule combinedProtocolSchedule =
combinedProtocolScheduleFactory.create(consensusSchedule, Optional.of(BigInteger.TEN));

// consensus schedule 1
assertThat(combinedProtocolSchedule.getByBlockNumber(0L).getName()).isEqualTo("Frontier");
assertThat(combinedProtocolSchedule.getByBlockNumber(0L))
.isSameAs(protocolSchedule1.getByBlockNumber(0L));

assertThat(combinedProtocolSchedule.getByBlockNumber(5L).getName()).isEqualTo("Homestead");
assertThat(combinedProtocolSchedule.getByBlockNumber(5L))
.isSameAs(protocolSchedule1.getByBlockNumber(5L));
assertThat(combinedProtocolSchedule.getByBlockNumber(10L).getName())
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(0L, 0L).getName())
.isEqualTo("Frontier");
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(0L, 0L))
.isSameAs(protocolSchedule1.getByBlockNumberOrTimestamp(0L, 0L));

assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(5L, 0L).getName())
.isEqualTo("Homestead");
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(5L, 0L))
.isSameAs(protocolSchedule1.getByBlockNumberOrTimestamp(5L, 0L));
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(10L, 0L).getName())
.isEqualTo("Constantinople");
assertThat(combinedProtocolSchedule.getByBlockNumber(10L))
.isSameAs(protocolSchedule1.getByBlockNumber(10L));
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(10L, 0L))
.isSameAs(protocolSchedule1.getByBlockNumberOrTimestamp(10L, 0L));

// consensus schedule 2 migration block
assertThat(combinedProtocolSchedule.getByBlockNumber(100L).getName())
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(100L, 0L).getName())
.isEqualTo("Constantinople");
assertThat(combinedProtocolSchedule.getByBlockNumber(100L))
.isSameAs(protocolSchedule2.getByBlockNumber(10L));
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(100L, 0L))
.isSameAs(protocolSchedule2.getByBlockNumberOrTimestamp(10L, 0L));

// consensus schedule 2
assertThat(combinedProtocolSchedule.getByBlockNumber(105L).getName()).isEqualTo("Byzantium");
assertThat(combinedProtocolSchedule.getByBlockNumber(105L))
.isSameAs(protocolSchedule2.getByBlockNumber(105L));
assertThat(combinedProtocolSchedule.getByBlockNumber(110L).getName()).isEqualTo("Berlin");
assertThat(combinedProtocolSchedule.getByBlockNumber(110L))
.isSameAs(protocolSchedule2.getByBlockNumber(110L));
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(105L, 0L).getName())
.isEqualTo("Byzantium");
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(105L, 0L))
.isSameAs(protocolSchedule2.getByBlockNumberOrTimestamp(105L, 0L));
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(110L, 0L).getName())
.isEqualTo("Berlin");
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(110L, 0L))
.isSameAs(protocolSchedule2.getByBlockNumberOrTimestamp(110L, 0L));

// consensus schedule 3 migration block
assertThat(combinedProtocolSchedule.getByBlockNumber(200L).getName()).isEqualTo("Berlin");
assertThat(combinedProtocolSchedule.getByBlockNumber(200L))
.isSameAs(protocolSchedule3.getByBlockNumber(110L));
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(200L, 0L).getName())
.isEqualTo("Berlin");
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(200L, 0L))
.isSameAs(protocolSchedule3.getByBlockNumberOrTimestamp(110L, 0L));

// consensus schedule 3
assertThat(combinedProtocolSchedule.getByBlockNumber(220L).getName()).isEqualTo("London");
assertThat(combinedProtocolSchedule.getByBlockNumber(220L))
.isSameAs(protocolSchedule3.getByBlockNumber(220L));
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(220L, 0L).getName())
.isEqualTo("London");
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(220L, 0L))
.isSameAs(protocolSchedule3.getByBlockNumberOrTimestamp(220L, 0L));

// consensus schedule 4
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(0L, 1000000050L).getName())
.isEqualTo("Shanghai");
assertThat(combinedProtocolSchedule.getByBlockNumberOrTimestamp(220L, 1000000050L))
.isSameAs(protocolSchedule4.getByBlockNumberOrTimestamp(220L, 1000000050L));

assertThat(
new MilestoneStreamingProtocolSchedule(combinedProtocolSchedule)
.streamMilestoneBlocks()
.collect(Collectors.toList()))
.isEqualTo(List.of(0L, 5L, 10L, 100L, 105L, 110L, 200L, 220L));
.isEqualTo(List.of(0L, 5L, 10L, 100L, 105L, 110L, 200L, 220L, 1000000000L, 1000000050L));
}

private BftProtocolSchedule createProtocolSchedule(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static org.mockito.Mockito.when;

import org.hyperledger.besu.consensus.common.bft.BftExtraData;
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.blockcreation.BftBlockCreator;
Expand All @@ -48,6 +49,7 @@
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.BlockImporter;
import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException;
import org.hyperledger.besu.util.Subscribers;
Expand All @@ -74,6 +76,8 @@ public class IbftRoundIntegrationTest {
private final Subscribers<MinedBlockObserver> subscribers = Subscribers.create();
private ProtocolContext protocolContext;

@Mock private BftProtocolSchedule protocolSchedule;
@Mock private ProtocolSpec protocolSpec;
@Mock private MutableBlockchain blockChain;
@Mock private WorldStateArchive worldStateArchive;
@Mock private BlockImporter blockImporter;
Expand Down Expand Up @@ -112,6 +116,9 @@ public void setup() {
final BlockHeader header = headerTestFixture.buildHeader();
proposedBlock = new Block(header, new BlockBody(emptyList(), emptyList()));

when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
when(protocolSpec.getBlockImporter()).thenReturn(blockImporter);

when(blockImporter.importBlock(any(), any(), any())).thenReturn(new BlockImportResult(true));

protocolContext =
Expand All @@ -131,7 +138,7 @@ public void signingFailsOnReceiptOfProposalUpdatesRoundButTransmitsNothing() {
roundState,
blockCreator,
protocolContext,
blockImporter,
protocolSchedule,
subscribers,
nodeKey,
throwingMessageFactory,
Expand All @@ -158,7 +165,7 @@ public void failuresToSignStillAllowBlockToBeImported() {
roundState,
blockCreator,
protocolContext,
blockImporter,
protocolSchedule,
subscribers,
nodeKey,
throwingMessageFactory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.hyperledger.besu.ethereum.core.BlockImporter;
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.plugin.services.securitymodule.SecurityModuleException;
import org.hyperledger.besu.util.Subscribers;

Expand All @@ -57,7 +58,7 @@ public class IbftRound {
private final RoundState roundState;
private final BlockCreator blockCreator;
private final ProtocolContext protocolContext;
private final BlockImporter blockImporter;
private final ProtocolSchedule protocolSchedule;
private final NodeKey nodeKey;
private final MessageFactory messageFactory; // used only to create stored local msgs
private final IbftMessageTransmitter transmitter;
Expand All @@ -69,7 +70,7 @@ public class IbftRound {
* @param roundState the round state
* @param blockCreator the block creator
* @param protocolContext the protocol context
* @param blockImporter the block importer
* @param protocolSchedule the protocol schedule
* @param observers the observers
* @param nodeKey the node key
* @param messageFactory the message factory
Expand All @@ -81,7 +82,7 @@ public IbftRound(
final RoundState roundState,
final BlockCreator blockCreator,
final ProtocolContext protocolContext,
final BlockImporter blockImporter,
final ProtocolSchedule protocolSchedule,
final Subscribers<MinedBlockObserver> observers,
final NodeKey nodeKey,
final MessageFactory messageFactory,
Expand All @@ -91,7 +92,7 @@ public IbftRound(
this.roundState = roundState;
this.blockCreator = blockCreator;
this.protocolContext = protocolContext;
this.blockImporter = blockImporter;
this.protocolSchedule = protocolSchedule;
this.observers = observers;
this.nodeKey = nodeKey;
this.messageFactory = messageFactory;
Expand Down Expand Up @@ -312,6 +313,8 @@ private void importBlockToChain() {
blockToImport.getHash());
}
LOG.trace("Importing block with extraData={}", extraData);
final BlockImporter blockImporter =
protocolSchedule.getByBlockHeader(blockToImport.getHeader()).getBlockImporter();
final BlockImportResult result =
blockImporter.importBlock(protocolContext, blockToImport, HeaderValidationMode.FULL);
if (!result.isImported()) {
Expand Down
Loading