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

[BESU-1919] Add proper support for eth_hashrate #1063

Merged
merged 15 commits into from
Jun 15, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ public void startNode(final BesuNode node) {
params.add("--min-gas-price");
params.add(
Integer.toString(node.getMiningParameters().getMinTransactionGasPrice().intValue()));
params.add("--Xminer-remote-sealers-limit");
params.add(Integer.toString(node.getMiningParameters().getRemoteSealersLimit()));
params.add("--Xminer-remote-sealers-hashrate-ttl");
params.add(Long.toString(node.getMiningParameters().getRemoteSealersTimeToLive()));
}
if (node.getMiningParameters().isStratumMiningEnabled()) {
params.add("--miner-stratum-enabled");
Expand Down
1 change: 1 addition & 0 deletions besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ public Runner build() {
Optional.of(
new StratumServer(
vertx,
miningCoordinator,
miningParameters.getStratumPort(),
miningParameters.getStratumNetworkInterface(),
miningParameters.getStratumExtranonce()));
Expand Down
22 changes: 20 additions & 2 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration.DEFAULT_JSON_RPC_PORT;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.DEFAULT_JSON_RPC_APIS;
import static org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration.DEFAULT_WEBSOCKET_PORT;
import static org.hyperledger.besu.ethereum.core.MiningParameters.DEFAULT_REMOTE_SEALERS_LIMIT;
import static org.hyperledger.besu.ethereum.core.MiningParameters.DEFAULT_REMOTE_SEALERS_TTL;
import static org.hyperledger.besu.metrics.BesuMetricCategory.DEFAULT_METRIC_CATEGORIES;
import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT;
import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PUSH_PORT;
Expand Down Expand Up @@ -724,6 +726,18 @@ void setBannedNodeIds(final List<String> values) {
description = "Stratum port binding (default: ${DEFAULT-VALUE})")
private final Integer stratumPort = 8008;

@Option(
names = {"--Xminer-remote-sealers-limit"},
description =
"Limits the number of remote sealers that can submit their hashrates (default: ${DEFAULT-VALUE})")
private final Integer remoteSealersLimit = DEFAULT_REMOTE_SEALERS_LIMIT;

@Option(
names = {"--Xminer-remote-sealers-hashrate-ttl"},
description =
"Specifies the lifetime of each entry in the cache. An entry will be automatically deleted if no update has been received before the deadline (default: ${DEFAULT-VALUE} minutes)")
private final Long remoteSealersTimeToLive = DEFAULT_REMOTE_SEALERS_TTL;

@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@Option(
hidden = true,
Expand Down Expand Up @@ -1320,7 +1334,9 @@ private void issueOptionWarnings() {
"--min-gas-price",
"--min-block-occupancy-ratio",
"--miner-extra-data",
"--miner-stratum-enabled"));
"--miner-stratum-enabled",
"--Xminer-remote-sealers-limit",
"--Xminer-remote-sealers-hashrate-ttl"));

CommandLineUtils.checkOptionDependencies(
logger,
Expand Down Expand Up @@ -1415,7 +1431,9 @@ public BesuControllerBuilder getControllerBuilder() {
stratumPort,
stratumExtranonce,
Optional.empty(),
minBlockOccupancyRatio))
minBlockOccupancyRatio,
remoteSealersLimit,
remoteSealersTimeToLive))
.transactionPoolConfiguration(buildTransactionPoolConfiguration())
.nodeKey(buildNodeKey())
.metricsSystem(metricsSystem.get())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

import static com.google.common.base.Preconditions.checkNotNull;
import static org.hyperledger.besu.cli.subcommands.blocks.BlocksSubCommand.COMMAND_NAME;
import static org.hyperledger.besu.ethereum.core.MiningParameters.DEFAULT_REMOTE_SEALERS_LIMIT;
import static org.hyperledger.besu.ethereum.core.MiningParameters.DEFAULT_REMOTE_SEALERS_TTL;

import org.hyperledger.besu.chainexport.RlpBlockExporter;
import org.hyperledger.besu.chainimport.JsonBlockImporter;
Expand Down Expand Up @@ -244,7 +246,9 @@ private MiningParameters getMiningParameters() {
8008,
"080c",
Optional.of(new IncrementingNonceGenerator(0)),
0.0);
0.0,
DEFAULT_REMOTE_SEALERS_LIMIT,
DEFAULT_REMOTE_SEALERS_TTL);
}

private void importJsonBlocks(final BesuController controller, final Path path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ protected MiningCoordinator createMiningCoordinator(
gasLimitCalculator);

final EthHashMiningCoordinator miningCoordinator =
new EthHashMiningCoordinator(protocolContext.getBlockchain(), executor, syncState);
new EthHashMiningCoordinator(
protocolContext.getBlockchain(),
executor,
syncState,
miningParameters.getRemoteSealersLimit(),
miningParameters.getRemoteSealersTimeToLive());
miningCoordinator.addMinedBlockObserver(ethProtocolManager);
miningCoordinator.setStratumMiningEnabled(miningParameters.isStratumMiningEnabled());
if (miningParameters.isMiningEnabled()) {
Expand Down
3 changes: 2 additions & 1 deletion besu/src/test/resources/everything_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ min-gas-price=1
min-block-occupancy-ratio=0.7
miner-stratum-host="0.0.0.0"
miner-stratum-port=8008

Xminer-remote-sealers-limit=1000
Xminer-remote-sealers-hashrate-ttl=10
# Pruning
pruning-enabled=true
pruning-blocks-retained=1024
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public enum RpcMethod {
ETH_GET_UNCLE_COUNT_BY_BLOCK_NUMBER("eth_getUncleCountByBlockNumber"),
ETH_GET_WORK("eth_getWork"),
ETH_HASHRATE("eth_hashrate"),
ETH_SUBMIT_HASHRATE("eth_submitHashrate"),
ETH_MINING("eth_mining"),
ETH_NEW_BLOCK_FILTER("eth_newBlockFilter"),
ETH_NEW_FILTER("eth_newFilter"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;

import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;

import org.apache.tuweni.bytes.Bytes;

public class EthSubmitHashRate implements JsonRpcMethod {

private final MiningCoordinator miningCoordinator;

public EthSubmitHashRate(final MiningCoordinator miningCoordinator) {
this.miningCoordinator = miningCoordinator;
}

@Override
public String getName() {
return RpcMethod.ETH_SUBMIT_HASHRATE.getMethodName();
}

@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
final String hashRate = requestContext.getRequiredParameter(0, String.class);
final String id = requestContext.getRequiredParameter(1, String.class);
return new JsonRpcSuccessResponse(
requestContext.getRequest().getId(),
miningCoordinator.submitHashRate(
id, Bytes.fromHexString(hashRate).toBigInteger().longValue()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthProtocolVersion;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthSendRawTransaction;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthSendTransaction;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthSubmitHashRate;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthSubmitWork;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthSyncing;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthUninstallFilter;
Expand Down Expand Up @@ -154,6 +155,7 @@ protected Map<String, JsonRpcMethod> create() {
new EthGetWork(miningCoordinator),
new EthSubmitWork(miningCoordinator),
new EthHashrate(miningCoordinator),
new EthSubmitHashRate(miningCoordinator),
new EthChainId(protocolSchedule.getChainId()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.ethereum.blockcreation;

import static org.apache.logging.log4j.LogManager.getLogger;

import org.hyperledger.besu.ethereum.chain.BlockAddedObserver;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address;
Expand All @@ -23,6 +25,11 @@
import org.hyperledger.besu.ethereum.mainnet.EthHashSolverInputs;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.logging.log4j.Logger;

/**
* Responsible for determining when a block mining operation should be started/stopped, then
Expand All @@ -31,13 +38,27 @@
public class EthHashMiningCoordinator extends AbstractMiningCoordinator<EthHashBlockMiner>
implements BlockAddedObserver {

private static final Logger LOG = getLogger();

private final EthHashMinerExecutor executor;

private final Cache<String, Long> sealerHashRate;

private volatile Optional<Long> cachedHashesPerSecond = Optional.empty();

public EthHashMiningCoordinator(
final Blockchain blockchain, final EthHashMinerExecutor executor, final SyncState syncState) {
final Blockchain blockchain,
final EthHashMinerExecutor executor,
final SyncState syncState,
final int remoteSealersLimit,
final long remoteSealersTimeToLive) {
super(blockchain, executor, syncState);
this.executor = executor;
this.sealerHashRate =
CacheBuilder.newBuilder()
.maximumSize(remoteSealersLimit)
.expireAfterWrite(remoteSealersTimeToLive, TimeUnit.MINUTES)
.build();
}

@Override
Expand All @@ -51,6 +72,18 @@ public void setStratumMiningEnabled(final boolean stratumMiningEnabled) {

@Override
public Optional<Long> hashesPerSecond() {
if (sealerHashRate.size() <= 0) {
return localHashesPerSecond();
} else {
return remoteHashesPerSecond();
}
}

private Optional<Long> remoteHashesPerSecond() {
return Optional.of(sealerHashRate.asMap().values().stream().mapToLong(Long::longValue).sum());
}

private Optional<Long> localHashesPerSecond() {
final Optional<Long> currentHashesPerSecond =
currentRunningMiner.flatMap(EthHashBlockMiner::getHashesPerSecond);

Expand All @@ -62,6 +95,16 @@ public Optional<Long> hashesPerSecond() {
}
}

@Override
public boolean submitHashRate(final String id, final Long hashrate) {
if (hashrate == 0) {
return false;
}
LOG.info("Hashrate submitted id {} hashrate {}", id, hashrate);
sealerHashRate.put(id, hashrate);
return true;
}

@Override
public Optional<EthHashSolverInputs> getWorkDefinition() {
return currentRunningMiner.flatMap(EthHashBlockMiner::getWorkDefinition);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ default boolean submitWork(final EthHashSolution solution) {
"Current consensus mechanism prevents submission of work solutions.");
}

/**
* Allows to submit the hashrate of a sealer with a specific id
*
* @param id of the sealer
* @param hashrate of the sealer
* @return true if the hashrate has been added otherwise false
*/
default boolean submitHashRate(final String id, final Long hashrate) {
return false;
}

/**
* Creates a block if possible, otherwise return an empty result
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package org.hyperledger.besu.ethereum.blockcreation;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.MiningParameters.DEFAULT_REMOTE_SEALERS_LIMIT;
import static org.hyperledger.besu.ethereum.core.MiningParameters.DEFAULT_REMOTE_SEALERS_TTL;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -45,7 +47,12 @@ public void setUp() {
@Test
public void miningCoordinatorIsCreatedDisabledWithNoReportableMiningStatistics() {
final EthHashMiningCoordinator miningCoordinator =
new EthHashMiningCoordinator(executionContext.getBlockchain(), executor, syncState);
new EthHashMiningCoordinator(
executionContext.getBlockchain(),
executor,
syncState,
DEFAULT_REMOTE_SEALERS_LIMIT,
DEFAULT_REMOTE_SEALERS_TTL);
final EthHashSolution solution = new EthHashSolution(1L, Hash.EMPTY, new byte[Bytes32.SIZE]);

assertThat(miningCoordinator.isMining()).isFalse();
Expand All @@ -66,7 +73,12 @@ public void reportedHashRateIsCachedIfNoCurrentDataInMiner() {
when(executor.startAsyncMining(any(), any(), any())).thenReturn(Optional.of(miner));

final EthHashMiningCoordinator miningCoordinator =
new EthHashMiningCoordinator(executionContext.getBlockchain(), executor, syncState);
new EthHashMiningCoordinator(
executionContext.getBlockchain(),
executor,
syncState,
DEFAULT_REMOTE_SEALERS_LIMIT,
DEFAULT_REMOTE_SEALERS_TTL);

// Must enable prior returning data
miningCoordinator.enable();
Expand Down
Loading