diff --git a/acceptance-tests/src/test-support/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java b/acceptance-tests/src/test-support/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java index a575dcccc1..c71e13e351 100644 --- a/acceptance-tests/src/test-support/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java +++ b/acceptance-tests/src/test-support/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java @@ -17,6 +17,7 @@ import tech.pegasys.pantheon.Runner; import tech.pegasys.pantheon.RunnerBuilder; import tech.pegasys.pantheon.cli.config.EthNetworkConfig; +import tech.pegasys.pantheon.controller.GasLimitCalculator; import tech.pegasys.pantheon.controller.KeyPairUtil; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.controller.PantheonControllerBuilder; @@ -146,6 +147,7 @@ public void startNode(final PantheonNode node) { .clock(Clock.systemUTC()) .isRevertReasonEnabled(node.isRevertReasonEnabled()) .storageProvider(storageProvider) + .targetGasLimit(GasLimitCalculator.DEFAULT) .build(); } catch (final IOException e) { throw new RuntimeException("Error building PantheonController", e); diff --git a/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutor.java b/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutor.java index 96514f3c24..6ad75b14cc 100644 --- a/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutor.java +++ b/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutor.java @@ -53,14 +53,16 @@ public CliqueMinerExecutor( final KeyPair nodeKeys, final MiningParameters miningParams, final AbstractBlockScheduler blockScheduler, - final EpochManager epochManager) { + final EpochManager epochManager, + final Function gasLimitCalculator) { super( protocolContext, executorService, protocolSchedule, pendingTransactions, miningParams, - blockScheduler); + blockScheduler, + gasLimitCalculator); this.nodeKeys = nodeKeys; this.localAddress = Util.publicKeyToAddress(nodeKeys.getPublicKey()); this.epochManager = epochManager; @@ -89,7 +91,7 @@ private CliqueBlockMiner createMiner( pendingTransactions, protocolContext, protocolSchedule, - (gasLimit) -> gasLimit, + gasLimitCalculator, nodeKeys, minTransactionGasPrice, header, diff --git a/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreatorTest.java b/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreatorTest.java index 3ed775bee8..bc1d3701fe 100644 --- a/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreatorTest.java +++ b/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreatorTest.java @@ -51,6 +51,7 @@ import tech.pegasys.pantheon.util.bytes.BytesValue; import java.util.List; +import java.util.function.Function; import com.google.common.collect.Lists; import org.junit.Before; @@ -121,7 +122,7 @@ public void proposerAddressCanBeExtractFromAConstructedBlock() { metricsSystem), protocolContext, protocolSchedule, - gasLimit -> gasLimit, + Function.identity(), proposerKeyPair, Wei.ZERO, blockchain.getChainHeadHeader(), @@ -152,7 +153,7 @@ public void insertsValidVoteIntoConstructedBlock() { metricsSystem), protocolContext, protocolSchedule, - gasLimit -> gasLimit, + Function.identity(), proposerKeyPair, Wei.ZERO, blockchain.getChainHeadHeader(), @@ -182,7 +183,7 @@ public void insertsNoVoteWhenAuthInValidators() { metricsSystem), protocolContext, protocolSchedule, - gasLimit -> gasLimit, + Function.identity(), proposerKeyPair, Wei.ZERO, blockchain.getChainHeadHeader(), @@ -215,7 +216,7 @@ public void insertsNoVoteWhenAtEpoch() { metricsSystem), protocolContext, protocolSchedule, - gasLimit -> gasLimit, + Function.identity(), proposerKeyPair, Wei.ZERO, blockchain.getChainHeadHeader(), diff --git a/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutorTest.java b/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutorTest.java index ab917aabc5..d2832834da 100644 --- a/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutorTest.java +++ b/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutorTest.java @@ -46,6 +46,7 @@ import java.util.List; import java.util.Random; import java.util.concurrent.Executors; +import java.util.function.Function; import com.google.common.collect.Lists; import org.junit.Before; @@ -98,7 +99,8 @@ public void extraDataCreatedOnEpochBlocksContainsValidators() { proposerKeyPair, new MiningParameters(AddressHelpers.ofValue(1), Wei.ZERO, vanityData, false), mock(CliqueBlockScheduler.class), - new EpochManager(EPOCH_LENGTH)); + new EpochManager(EPOCH_LENGTH), + Function.identity()); // NOTE: Passing in the *parent* block, so must be 1 less than EPOCH final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH - 1).buildHeader(); @@ -135,7 +137,8 @@ public void extraDataForNonEpochBlocksDoesNotContainValidaors() { proposerKeyPair, new MiningParameters(AddressHelpers.ofValue(1), Wei.ZERO, vanityData, false), mock(CliqueBlockScheduler.class), - new EpochManager(EPOCH_LENGTH)); + new EpochManager(EPOCH_LENGTH), + Function.identity()); // Parent block was epoch, so the next block should contain no validators. final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH).buildHeader(); @@ -172,7 +175,8 @@ public void shouldUseLatestVanityData() { proposerKeyPair, new MiningParameters(AddressHelpers.ofValue(1), Wei.ZERO, initialVanityData, false), mock(CliqueBlockScheduler.class), - new EpochManager(EPOCH_LENGTH)); + new EpochManager(EPOCH_LENGTH), + Function.identity()); executor.setExtraData(modifiedVanityData); final BytesValue extraDataBytes = executor.calculateExtraData(blockHeaderBuilder.buildHeader()); diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContextBuilder.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContextBuilder.java index 655982a4a1..78ffed274f 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContextBuilder.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContextBuilder.java @@ -81,6 +81,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.Executors; +import java.util.function.Function; import java.util.stream.Collectors; import com.google.common.collect.Iterables; @@ -290,7 +291,7 @@ private static ControllerAndState createControllerAndFinalState( final IbftBlockCreatorFactory blockCreatorFactory = new IbftBlockCreatorFactory( - (gasLimit) -> gasLimit, + Function.identity(), pendingTransactions, // changed from IbftPantheonController protocolContext, protocolSchedule, diff --git a/ethereum/blockcreation/src/main/java/tech/pegasys/pantheon/ethereum/blockcreation/AbstractMinerExecutor.java b/ethereum/blockcreation/src/main/java/tech/pegasys/pantheon/ethereum/blockcreation/AbstractMinerExecutor.java index f92b660481..9f1dc57443 100644 --- a/ethereum/blockcreation/src/main/java/tech/pegasys/pantheon/ethereum/blockcreation/AbstractMinerExecutor.java +++ b/ethereum/blockcreation/src/main/java/tech/pegasys/pantheon/ethereum/blockcreation/AbstractMinerExecutor.java @@ -25,6 +25,7 @@ import java.util.Optional; import java.util.concurrent.ExecutorService; +import java.util.function.Function; public abstract class AbstractMinerExecutor< C, M extends BlockMiner>> { @@ -34,6 +35,7 @@ public abstract class AbstractMinerExecutor< protected final ProtocolSchedule protocolSchedule; protected final PendingTransactions pendingTransactions; protected final AbstractBlockScheduler blockScheduler; + protected final Function gasLimitCalculator; protected volatile BytesValue extraData; protected volatile Wei minTransactionGasPrice; @@ -44,7 +46,8 @@ public AbstractMinerExecutor( final ProtocolSchedule protocolSchedule, final PendingTransactions pendingTransactions, final MiningParameters miningParams, - final AbstractBlockScheduler blockScheduler) { + final AbstractBlockScheduler blockScheduler, + final Function gasLimitCalculator) { this.protocolContext = protocolContext; this.executorService = executorService; this.protocolSchedule = protocolSchedule; @@ -52,6 +55,7 @@ public AbstractMinerExecutor( this.extraData = miningParams.getExtraData(); this.minTransactionGasPrice = miningParams.getMinTransactionGasPrice(); this.blockScheduler = blockScheduler; + this.gasLimitCalculator = gasLimitCalculator; } public abstract M startAsyncMining( diff --git a/ethereum/blockcreation/src/main/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashMinerExecutor.java b/ethereum/blockcreation/src/main/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashMinerExecutor.java index 6041890cf8..f79ad268b6 100644 --- a/ethereum/blockcreation/src/main/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashMinerExecutor.java +++ b/ethereum/blockcreation/src/main/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashMinerExecutor.java @@ -37,14 +37,16 @@ public EthHashMinerExecutor( final ProtocolSchedule protocolSchedule, final PendingTransactions pendingTransactions, final MiningParameters miningParams, - final AbstractBlockScheduler blockScheduler) { + final AbstractBlockScheduler blockScheduler, + final Function gasLimitCalculator) { super( protocolContext, executorService, protocolSchedule, pendingTransactions, miningParams, - blockScheduler); + blockScheduler, + gasLimitCalculator); this.coinbase = miningParams.getCoinbase(); } @@ -77,7 +79,7 @@ private EthHashBlockMiner createMiner( pendingTransactions, protocolContext, protocolSchedule, - (gasLimit) -> gasLimit, + gasLimitCalculator, solver, minTransactionGasPrice, parentHeader); diff --git a/ethereum/blockcreation/src/test/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashMinerExecutorTest.java b/ethereum/blockcreation/src/test/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashMinerExecutorTest.java index a67f42e5db..c7001df951 100644 --- a/ethereum/blockcreation/src/test/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashMinerExecutorTest.java +++ b/ethereum/blockcreation/src/test/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashMinerExecutorTest.java @@ -24,6 +24,7 @@ import tech.pegasys.pantheon.util.Subscribers; import java.util.concurrent.Executors; +import java.util.function.Function; import org.junit.Test; @@ -49,7 +50,8 @@ public void startingMiningWithoutCoinbaseThrowsException() { null, pendingTransactions, miningParameters, - new DefaultBlockScheduler(1, 10, TestClock.fixed())); + new DefaultBlockScheduler(1, 10, TestClock.fixed()), + Function.identity()); assertThatExceptionOfType(CoinbaseNotSetException.class) .isThrownBy(() -> executor.startAsyncMining(Subscribers.create(), null)) @@ -74,7 +76,8 @@ public void settingCoinbaseToNullThrowsException() { null, pendingTransactions, miningParameters, - new DefaultBlockScheduler(1, 10, TestClock.fixed())); + new DefaultBlockScheduler(1, 10, TestClock.fixed()), + Function.identity()); assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> executor.setCoinbase(null)) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index ea182da6c5..24cce6ac4b 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -641,6 +641,12 @@ void setBannedNodeIds(final List values) { "The name of a file containing the private key used to sign privacy marker transactions. If unset, each will be signed with a random key.") private final Path privacyMarkerTransactionSigningKeyPath = null; + @Option( + names = {"--target-gas-limit"}, + description = + "Sets target gas limit per block. If set each blocks gas limit will approach this setting over time if the current gas limit is different.") + private final Long targetGasLimit = null; + @Option( names = {"--tx-pool-max-size"}, paramLabel = MANDATORY_INTEGER_FORMAT_HELP, @@ -1011,7 +1017,8 @@ public PantheonControllerBuilder getControllerBuilder() { .storageProvider(keyStorageProvider(keyValueStorageName)) .isPruningEnabled(isPruningEnabled) .pruningConfiguration(buildPruningConfiguration()) - .genesisConfigOverrides(genesisConfigOverrides); + .genesisConfigOverrides(genesisConfigOverrides) + .targetGasLimit(targetGasLimit); } catch (final IOException e) { throw new ExecutionException(this.commandLine, "Invalid path", e); } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonControllerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonControllerBuilder.java index 5791d177bf..a12c7de419 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonControllerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonControllerBuilder.java @@ -94,7 +94,8 @@ protected MiningCoordinator createMiningCoordinator( protocolContext.getConsensusState().getVoteTallyCache(), localAddress, secondsBetweenBlocks), - epochManager); + epochManager, + gasLimitCalculator); final CliqueMiningCoordinator miningCoordinator = new CliqueMiningCoordinator( protocolContext.getBlockchain(), diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/GasLimitCalculator.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/GasLimitCalculator.java new file mode 100644 index 0000000000..c71102b2ac --- /dev/null +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/GasLimitCalculator.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.controller; + +import java.util.function.Function; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class GasLimitCalculator implements Function { + private static final Logger LOG = LogManager.getLogger(); + public static final long ADJUSTMENT_FACTOR = 1024L; + public static final Long DEFAULT = null; + private final long targetGasLimit; + + public GasLimitCalculator(final Long targetGasLimit) { + this.targetGasLimit = targetGasLimit == null ? 0L : targetGasLimit; + } + + @Override + public Long apply(final Long gasLimit) { + long newGasLimit; + + if (targetGasLimit > gasLimit) { + newGasLimit = Math.min(targetGasLimit, gasLimit + ADJUSTMENT_FACTOR); + } else if (targetGasLimit < gasLimit) { + newGasLimit = Math.max(targetGasLimit, gasLimit - ADJUSTMENT_FACTOR); + } else { + return gasLimit; + } + + LOG.debug("Adjusting block gas limit from {} to {}", gasLimit, newGasLimit); + + return newGasLimit; + } +} diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonControllerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonControllerBuilder.java index ef5f2ae22b..35a69a167a 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonControllerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonControllerBuilder.java @@ -111,7 +111,7 @@ protected MiningCoordinator createMiningCoordinator( final IbftBlockCreatorFactory blockCreatorFactory = new IbftBlockCreatorFactory( - (gasLimit) -> gasLimit, + gasLimitCalculator, transactionPool.getPendingTransactions(), protocolContext, protocolSchedule, diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/MainnetPantheonControllerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/MainnetPantheonControllerBuilder.java index ae9a898bc8..8f25b5a09b 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/MainnetPantheonControllerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/MainnetPantheonControllerBuilder.java @@ -56,7 +56,8 @@ protected MiningCoordinator createMiningCoordinator( new DefaultBlockScheduler( MainnetBlockHeaderValidator.MINIMUM_SECONDS_SINCE_PARENT, MainnetBlockHeaderValidator.TIMESTAMP_TOLERANCE_S, - clock)); + clock), + gasLimitCalculator); final EthHashMiningCoordinator miningCoordinator = new EthHashMiningCoordinator(protocolContext.getBlockchain(), executor, syncState); diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java index f7a75a78bb..5ef6636347 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonControllerBuilder.java @@ -83,6 +83,7 @@ public abstract class PantheonControllerBuilder { protected Clock clock; protected KeyPair nodeKeys; protected boolean isRevertReasonEnabled; + protected GasLimitCalculator gasLimitCalculator; private StorageProvider storageProvider; private final List shutdownActions = new ArrayList<>(); private boolean isPruningEnabled; @@ -180,6 +181,11 @@ public PantheonControllerBuilder genesisConfigOverrides( return this; } + public PantheonControllerBuilder targetGasLimit(final Long targetGasLimit) { + this.gasLimitCalculator = new GasLimitCalculator(targetGasLimit); + return this; + } + public PantheonController build() { checkNotNull(genesisConfig, "Missing genesis config"); checkNotNull(syncConfig, "Missing sync config"); @@ -193,6 +199,7 @@ public PantheonController build() { checkNotNull(transactionPoolConfiguration, "Missing transaction pool configuration"); checkNotNull(nodeKeys, "Missing node keys"); checkNotNull(storageProvider, "Must supply a storage provider"); + checkNotNull(gasLimitCalculator, "Missing gas limit calculator"); prepForBuild(); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/PrivacyTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/PrivacyTest.java index 032c2ff7df..d5503a7873 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/PrivacyTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/PrivacyTest.java @@ -15,6 +15,7 @@ import static org.assertj.core.api.Assertions.assertThat; import tech.pegasys.pantheon.config.GenesisConfigFile; +import tech.pegasys.pantheon.controller.GasLimitCalculator; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.core.Account; @@ -77,6 +78,7 @@ public void privacyPrecompiled() throws IOException { .clock(TestClock.fixed()) .privacyParameters(privacyParameters) .transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) + .targetGasLimit(GasLimitCalculator.DEFAULT) .build(); final Address privacyContractAddress = Address.privacyPrecompiled(ADDRESS); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index 3fac6e9b81..f5610fbf40 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -20,6 +20,7 @@ import tech.pegasys.pantheon.cli.config.EthNetworkConfig; import tech.pegasys.pantheon.config.GenesisConfigFile; +import tech.pegasys.pantheon.controller.GasLimitCalculator; import tech.pegasys.pantheon.controller.MainnetPantheonControllerBuilder; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; @@ -143,6 +144,7 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { .clock(TestClock.fixed()) .transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) .storageProvider(createKeyValueStorageProvider(dbAhead)) + .targetGasLimit(GasLimitCalculator.DEFAULT) .build()) { setupState(blockCount, controller.getProtocolSchedule(), controller.getProtocolContext()); } @@ -162,6 +164,7 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { .clock(TestClock.fixed()) .transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) .storageProvider(createKeyValueStorageProvider(dbAhead)) + .targetGasLimit(GasLimitCalculator.DEFAULT) .build(); final String listenHost = InetAddress.getLoopbackAddress().getHostAddress(); final JsonRpcConfiguration aheadJsonRpcConfiguration = jsonRpcConfiguration(); @@ -220,6 +223,7 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { .privacyParameters(PrivacyParameters.DEFAULT) .clock(TestClock.fixed()) .transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) + .targetGasLimit(GasLimitCalculator.DEFAULT) .build(); final EnodeURL enode = runnerAhead.getLocalEnode().get(); final EthNetworkConfig behindEthNetworkConfiguration = diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/chainexport/RlpBlockExporterTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/chainexport/RlpBlockExporterTest.java index da226eeb86..70416835d7 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/chainexport/RlpBlockExporterTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/chainexport/RlpBlockExporterTest.java @@ -17,6 +17,7 @@ import tech.pegasys.pantheon.chainimport.RlpBlockImporter; import tech.pegasys.pantheon.config.GenesisConfigFile; +import tech.pegasys.pantheon.controller.GasLimitCalculator; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.chain.Blockchain; @@ -88,6 +89,7 @@ private static PantheonController createController() throws IOException { .dataDirectory(dataDir) .clock(TestClock.fixed()) .transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) + .targetGasLimit(GasLimitCalculator.DEFAULT) .build(); } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/chainimport/JsonBlockImporterTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/chainimport/JsonBlockImporterTest.java index ff286a749e..1cb9a29971 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/chainimport/JsonBlockImporterTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/chainimport/JsonBlockImporterTest.java @@ -18,6 +18,7 @@ import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.config.JsonUtil; +import tech.pegasys.pantheon.controller.GasLimitCalculator; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.chain.Blockchain; @@ -425,6 +426,7 @@ protected PantheonController createController(final GenesisConfigFile genesis .dataDirectory(dataDir) .clock(TestClock.fixed()) .transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) + .targetGasLimit(GasLimitCalculator.DEFAULT) .build(); } } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/chainimport/RlpBlockImporterTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/chainimport/RlpBlockImporterTest.java index 9c4f31dc4a..1ecec4afa5 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/chainimport/RlpBlockImporterTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/chainimport/RlpBlockImporterTest.java @@ -16,6 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; import tech.pegasys.pantheon.config.GenesisConfigFile; +import tech.pegasys.pantheon.controller.GasLimitCalculator; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider; @@ -66,6 +67,7 @@ public void blockImport() throws IOException { .dataDirectory(dataDir) .clock(TestClock.fixed()) .transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) + .targetGasLimit(GasLimitCalculator.DEFAULT) .build(); final RlpBlockImporter.ImportResult result = rlpBlockImporter.importBlockchain(source, targetController); @@ -105,6 +107,7 @@ public void ibftImport() throws IOException { .dataDirectory(dataDir) .clock(TestClock.fixed()) .transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) + .targetGasLimit(GasLimitCalculator.DEFAULT) .build(); final RlpBlockImporter.ImportResult result = rlpBlockImporter.importBlockchain(source, controller); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java index 12dc3abe1c..56be4f041a 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java @@ -168,6 +168,7 @@ public void initMocks() throws Exception { when(mockControllerBuilder.isPruningEnabled(anyBoolean())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.pruningConfiguration(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.genesisConfigOverrides(any())).thenReturn(mockControllerBuilder); + when(mockControllerBuilder.targetGasLimit(any())).thenReturn(mockControllerBuilder); // doReturn used because of generic PantheonController doReturn(mockController).when(mockControllerBuilder).build(); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index 0af886669b..9f8bb6fd2f 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNotNull; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -174,6 +175,7 @@ public void callingPantheonCommandWithoutOptionsMustSyncWithDefaultValues() thro verify(mockControllerBuilder).miningParameters(miningArg.capture()); verify(mockControllerBuilder).nodePrivateKeyFile(isNotNull()); verify(mockControllerBuilder).storageProvider(storageProviderArgumentCaptor.capture()); + verify(mockControllerBuilder).targetGasLimit(isNull()); verify(mockControllerBuilder).build(); assertThat(storageProviderArgumentCaptor.getValue()).isNotNull(); @@ -2737,4 +2739,34 @@ public void tomlThatHasInvalidOptions() throws IOException { assertThat(commandErrorOutput.toString()) .contains("Unknown options in TOML configuration file: invalid_option, invalid_option2"); } + + @Test + public void targetGasLimitIsEnabledWhenSpecified() throws Exception { + parseCommand("--target-gas-limit=10000000"); + + final ArgumentCaptor targetGasLimitArg = ArgumentCaptor.forClass(Long.class); + + verify(mockControllerBuilder).targetGasLimit(targetGasLimitArg.capture()); + verify(mockControllerBuilder).build(); + + assertThat(commandOutput.toString()).isEmpty(); + assertThat(commandErrorOutput.toString()).isEmpty(); + + assertThat(targetGasLimitArg.getValue()).isEqualTo(10_000_000L); + } + + @Test + public void targetGasLimitIsDisabledWhenNotSpecified() throws Exception { + parseCommand(); + + final ArgumentCaptor targetGasLimitArg = ArgumentCaptor.forClass(Long.class); + + verify(mockControllerBuilder).targetGasLimit(targetGasLimitArg.capture()); + verify(mockControllerBuilder).build(); + + assertThat(commandOutput.toString()).isEmpty(); + assertThat(commandErrorOutput.toString()).isEmpty(); + + assertThat(targetGasLimitArg.getValue()).isEqualTo(null); + } } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/controller/GasLimitCalculatorTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/controller/GasLimitCalculatorTest.java new file mode 100644 index 0000000000..9f3a86227f --- /dev/null +++ b/pantheon/src/test/java/tech/pegasys/pantheon/controller/GasLimitCalculatorTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.controller; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.Function; + +import org.junit.Test; + +public class GasLimitCalculatorTest { + @Test + public void verifyGasLimitIsIncreasedWithinLimits() { + Function gasLimitCalculator = new GasLimitCalculator(10_000_000L); + assertThat(gasLimitCalculator.apply(8_000_000L)) + .isEqualTo(8_000_000L + GasLimitCalculator.ADJUSTMENT_FACTOR); + } + + @Test + public void verifyGasLimitIsDecreasedWithinLimits() { + Function gasLimitCalculator = new GasLimitCalculator(10_000_000L); + assertThat(gasLimitCalculator.apply(12_000_000L)) + .isEqualTo(12_000_000L - GasLimitCalculator.ADJUSTMENT_FACTOR); + } + + @Test + public void verifyGasLimitReachesTarget() { + final long target = 10_000_000L; + final long offset = GasLimitCalculator.ADJUSTMENT_FACTOR / 2; + Function gasLimitCalculator = new GasLimitCalculator(target); + assertThat(gasLimitCalculator.apply(target - offset)).isEqualTo(target); + assertThat(gasLimitCalculator.apply(target + offset)).isEqualTo(target); + } +} diff --git a/pantheon/src/test/resources/everything_config.toml b/pantheon/src/test/resources/everything_config.toml index 7c94010ea2..398ae0b328 100644 --- a/pantheon/src/test/resources/everything_config.toml +++ b/pantheon/src/test/resources/everything_config.toml @@ -107,4 +107,7 @@ Xincoming-tx-messages-keep-alive-seconds=60 revert-reason-enabled=false # Storage plugin to use -key-value-storage="rocksdb" \ No newline at end of file +key-value-storage="rocksdb" + +# Gas limit +target-gas-limit=8000000 \ No newline at end of file