Implement transitions for IBFT2 block rewards (#1977)

Signed-off-by: Vladyslav Lupashevskyi <vlad@lupashevskyi.com>
Co-authored-by: Trent Mohay <trent.mohay@consensys.net>
pull/2390/head
Vladyslav Lupashevskyi 3 years ago committed by GitHub
parent 52d3ac6aa2
commit 2b24f43a8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 16
      config/src/main/java/org/hyperledger/besu/config/BftFork.java
  3. 58
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftProtocolSchedule.java
  4. 43
      consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/BftProtocolScheduleTest.java

@ -4,6 +4,7 @@
### Additions and Improvements ### Additions and Improvements
- eip-1559 changes: accept transactions which have maxFeePerGas below current baseFee [\#2374](https://github.com/hyperledger/besu/pull/2374) - eip-1559 changes: accept transactions which have maxFeePerGas below current baseFee [\#2374](https://github.com/hyperledger/besu/pull/2374)
- Introduced transitions for IBFT2 block rewards [\#1977](https://github.com/hyperledger/besu/pull/1977)
### Bug Fixes ### Bug Fixes

@ -14,6 +14,7 @@
*/ */
package org.hyperledger.besu.config; package org.hyperledger.besu.config;
import java.math.BigInteger;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalInt; import java.util.OptionalInt;
@ -22,12 +23,14 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.apache.tuweni.bytes.Bytes;
public class BftFork { public class BftFork {
private static final String FORK_BLOCK_KEY = "block"; private static final String FORK_BLOCK_KEY = "block";
private static final String VALIDATORS_KEY = "validators"; private static final String VALIDATORS_KEY = "validators";
private static final String BLOCK_PERIOD_SECONDS_KEY = "blockperiodseconds"; private static final String BLOCK_PERIOD_SECONDS_KEY = "blockperiodseconds";
private static final String BLOCK_REWARD_KEY = "blockreward";
private final ObjectNode forkConfigRoot; private final ObjectNode forkConfigRoot;
@ -48,6 +51,19 @@ public class BftFork {
return JsonUtil.getInt(forkConfigRoot, BLOCK_PERIOD_SECONDS_KEY); return JsonUtil.getInt(forkConfigRoot, BLOCK_PERIOD_SECONDS_KEY);
} }
public Optional<BigInteger> getBlockRewardWei() {
final Optional<String> configFileContent = JsonUtil.getString(forkConfigRoot, BLOCK_REWARD_KEY);
if (configFileContent.isEmpty()) {
return Optional.empty();
}
final String weiStr = configFileContent.get();
if (weiStr.startsWith("0x")) {
return Optional.of(new BigInteger(1, Bytes.fromHexStringLenient(weiStr).toArrayUnsafe()));
}
return Optional.of(new BigInteger(weiStr));
}
public Optional<List<String>> getValidators() throws IllegalArgumentException { public Optional<List<String>> getValidators() throws IllegalArgumentException {
final Optional<ArrayNode> validatorNode = JsonUtil.getArrayNode(forkConfigRoot, VALIDATORS_KEY); final Optional<ArrayNode> validatorNode = JsonUtil.getArrayNode(forkConfigRoot, VALIDATORS_KEY);

@ -15,6 +15,7 @@
package org.hyperledger.besu.consensus.common.bft; package org.hyperledger.besu.consensus.common.bft;
import org.hyperledger.besu.config.BftConfigOptions; import org.hyperledger.besu.config.BftConfigOptions;
import org.hyperledger.besu.config.BftFork;
import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters;
@ -29,7 +30,11 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
/** Defines the protocol behaviours for a blockchain using a BFT consensus mechanism. */ /** Defines the protocol behaviours for a blockchain using a BFT consensus mechanism. */
public class BftProtocolSchedule { public class BftProtocolSchedule {
@ -42,19 +47,47 @@ public class BftProtocolSchedule {
final boolean isRevertReasonEnabled, final boolean isRevertReasonEnabled,
final Function<Integer, BlockHeaderValidator.Builder> blockHeaderRuleset, final Function<Integer, BlockHeaderValidator.Builder> blockHeaderRuleset,
final BftExtraDataCodec bftExtraDataCodec) { final BftExtraDataCodec bftExtraDataCodec) {
final Map<Long, Function<ProtocolSpecBuilder, ProtocolSpecBuilder>> specMap = new HashMap<>();
specMap.put(
0L,
builder ->
applyBftChanges(
config.getBftConfigOptions(),
builder,
config.isQuorum(),
blockHeaderRuleset,
bftExtraDataCodec,
config.getBftConfigOptions().getBlockRewardWei()));
final Supplier<List<BftFork>> forks;
if (config.isIbft2()) {
forks = () -> config.getTransitions().getIbftForks();
} else {
forks = () -> config.getTransitions().getQbftForks();
}
forks.get().stream()
.filter(fork -> fork.getBlockRewardWei().isPresent())
.forEach(
fork ->
specMap.put(
fork.getForkBlock(),
builder ->
applyBftChanges(
config.getBftConfigOptions(),
builder,
config.isQuorum(),
blockHeaderRuleset,
bftExtraDataCodec,
fork.getBlockRewardWei().get())));
final ProtocolSpecAdapters specAdapters = new ProtocolSpecAdapters(specMap);
return new ProtocolScheduleBuilder( return new ProtocolScheduleBuilder(
config, config,
DEFAULT_CHAIN_ID, DEFAULT_CHAIN_ID,
ProtocolSpecAdapters.create( specAdapters,
0,
builder ->
applyBftChanges(
config.getBftConfigOptions(),
builder,
config.isQuorum(),
blockHeaderRuleset,
bftExtraDataCodec)),
privacyParameters, privacyParameters,
isRevertReasonEnabled, isRevertReasonEnabled,
config.isQuorum()) config.isQuorum())
@ -86,13 +119,14 @@ public class BftProtocolSchedule {
final ProtocolSpecBuilder builder, final ProtocolSpecBuilder builder,
final boolean goQuorumMode, final boolean goQuorumMode,
final Function<Integer, BlockHeaderValidator.Builder> blockHeaderRuleset, final Function<Integer, BlockHeaderValidator.Builder> blockHeaderRuleset,
final BftExtraDataCodec bftExtraDataCodec) { final BftExtraDataCodec bftExtraDataCodec,
final BigInteger blockReward) {
if (configOptions.getEpochLength() <= 0) { if (configOptions.getEpochLength() <= 0) {
throw new IllegalArgumentException("Epoch length in config must be greater than zero"); throw new IllegalArgumentException("Epoch length in config must be greater than zero");
} }
if (configOptions.getBlockRewardWei().signum() < 0) { if (blockReward.signum() < 0) {
throw new IllegalArgumentException("Bft Block reward in config cannot be negative"); throw new IllegalArgumentException("Bft Block reward in config cannot be negative");
} }
@ -105,7 +139,7 @@ public class BftProtocolSchedule {
.blockValidatorBuilder(MainnetProtocolSpecs.blockValidatorBuilder(goQuorumMode)) .blockValidatorBuilder(MainnetProtocolSpecs.blockValidatorBuilder(goQuorumMode))
.blockImporterBuilder(MainnetBlockImporter::new) .blockImporterBuilder(MainnetBlockImporter::new)
.difficultyCalculator((time, parent, protocolContext) -> BigInteger.ONE) .difficultyCalculator((time, parent, protocolContext) -> BigInteger.ONE)
.blockReward(Wei.of(configOptions.getBlockRewardWei())) .blockReward(Wei.of(blockReward))
.skipZeroBlockRewards(true) .skipZeroBlockRewards(true)
.blockHeaderFunctions(BftBlockHeaderFunctions.forOnChainBlock(bftExtraDataCodec)); .blockHeaderFunctions(BftBlockHeaderFunctions.forOnChainBlock(bftExtraDataCodec));

@ -21,15 +21,19 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.hyperledger.besu.config.BftConfigOptions; import org.hyperledger.besu.config.BftConfigOptions;
import org.hyperledger.besu.config.BftFork;
import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.TransitionsConfigOptions;
import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator; import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.MutableProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.junit.Test; import org.junit.Test;
@ -51,6 +55,7 @@ public class BftProtocolScheduleTest {
when(configOptions.getMiningBeneficiary()).thenReturn(Optional.of(miningBeneficiary)); when(configOptions.getMiningBeneficiary()).thenReturn(Optional.of(miningBeneficiary));
when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward); when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward);
when(configOptions.getEpochLength()).thenReturn(3000L); when(configOptions.getEpochLength()).thenReturn(3000L);
when(genesisConfig.getTransitions()).thenReturn(TransitionsConfigOptions.DEFAULT);
when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions); when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions);
@ -74,7 +79,7 @@ public class BftProtocolScheduleTest {
when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions); when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions);
when(configOptions.getEpochLength()).thenReturn(3000L); when(configOptions.getEpochLength()).thenReturn(3000L);
when(configOptions.getBlockRewardWei()).thenReturn(BigInteger.ZERO); when(configOptions.getBlockRewardWei()).thenReturn(BigInteger.ZERO);
when(genesisConfig.getTransitions()).thenReturn(TransitionsConfigOptions.DEFAULT);
assertThatThrownBy( assertThatThrownBy(
() -> () ->
BftProtocolSchedule.create( BftProtocolSchedule.create(
@ -93,6 +98,7 @@ public class BftProtocolScheduleTest {
when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward); when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward);
when(configOptions.getEpochLength()).thenReturn(3000L); when(configOptions.getEpochLength()).thenReturn(3000L);
when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions); when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions);
when(genesisConfig.getTransitions()).thenReturn(TransitionsConfigOptions.DEFAULT);
final ProtocolSchedule schedule = final ProtocolSchedule schedule =
BftProtocolSchedule.create( BftProtocolSchedule.create(
@ -116,6 +122,7 @@ public class BftProtocolScheduleTest {
when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward); when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward);
when(configOptions.getEpochLength()).thenReturn(3000L); when(configOptions.getEpochLength()).thenReturn(3000L);
when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions); when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions);
when(genesisConfig.getTransitions()).thenReturn(TransitionsConfigOptions.DEFAULT);
assertThatThrownBy( assertThatThrownBy(
() -> () ->
@ -135,6 +142,7 @@ public class BftProtocolScheduleTest {
when(configOptions.getEpochLength()).thenReturn(0L); when(configOptions.getEpochLength()).thenReturn(0L);
when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward); when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward);
when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions); when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions);
when(genesisConfig.getTransitions()).thenReturn(TransitionsConfigOptions.DEFAULT);
assertThatThrownBy( assertThatThrownBy(
() -> () ->
@ -154,6 +162,7 @@ public class BftProtocolScheduleTest {
when(configOptions.getEpochLength()).thenReturn(-3000L); when(configOptions.getEpochLength()).thenReturn(-3000L);
when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward); when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward);
when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions); when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions);
when(genesisConfig.getTransitions()).thenReturn(TransitionsConfigOptions.DEFAULT);
assertThatThrownBy( assertThatThrownBy(
() -> () ->
@ -164,4 +173,36 @@ public class BftProtocolScheduleTest {
.isInstanceOf(IllegalArgumentException.class) .isInstanceOf(IllegalArgumentException.class)
.hasMessage("Epoch length in config must be greater than zero"); .hasMessage("Epoch length in config must be greater than zero");
} }
@Test
public void blockRewardSpecifiedInTransitionCreatesNewMilestone() {
final BigInteger arbitraryBlockReward = BigInteger.valueOf(5);
final String miningBeneficiary = Address.fromHexString("0x1").toString();
final BftConfigOptions configOptions = mock(BftConfigOptions.class);
when(configOptions.getMiningBeneficiary()).thenReturn(Optional.of(miningBeneficiary));
when(configOptions.getBlockRewardWei()).thenReturn(arbitraryBlockReward);
when(configOptions.getEpochLength()).thenReturn(3000L);
when(genesisConfig.getBftConfigOptions()).thenReturn(configOptions);
when(genesisConfig.isIbft2()).thenReturn(true);
final long transitionBlock = 5L;
final BftFork fork = mock(BftFork.class);
when(fork.getForkBlock()).thenReturn(transitionBlock);
final BigInteger forkBlockReward = arbitraryBlockReward.multiply(BigInteger.valueOf(2));
when(fork.getBlockRewardWei()).thenReturn(Optional.of(forkBlockReward));
final TransitionsConfigOptions transitions = mock(TransitionsConfigOptions.class);
when(transitions.getIbftForks()).thenReturn(List.of(fork));
when(genesisConfig.getTransitions()).thenReturn(transitions);
final MutableProtocolSchedule schedule =
(MutableProtocolSchedule)
BftProtocolSchedule.create(
genesisConfig, BftProtocolScheduleTest::arbitraryRulesetBuilder, bftExtraDataCodec);
assertThat(schedule.streamMilestoneBlocks().count()).isEqualTo(2);
assertThat(schedule.getByBlockNumber(0).getBlockReward())
.isEqualTo(Wei.of(arbitraryBlockReward));
assertThat(schedule.getByBlockNumber(transitionBlock).getBlockReward())
.isEqualTo(Wei.of(forkBlockReward));
}
} }

Loading…
Cancel
Save