diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java index 621e80d9b0..b01c9d93cb 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java @@ -84,8 +84,8 @@ public void startNode(final PantheonNode node) { runnerBuilder .vertx(Vertx.vertx()) .pantheonController(pantheonController) + .ethNetworkConfig(ethNetworkConfig) .discovery(node.isDiscoveryEnabled()) - .bootstrapPeers(node.bootnodes()) .discoveryHost(node.hostName()) .discoveryPort(node.p2pPort()) .maxPeers(25) diff --git a/config/src/main/java/tech/pegasys/pantheon/config/CliqueConfigOptions.java b/config/src/main/java/tech/pegasys/pantheon/config/CliqueConfigOptions.java index 242d4c5348..f318dd5af6 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/CliqueConfigOptions.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/CliqueConfigOptions.java @@ -12,6 +12,9 @@ */ package tech.pegasys.pantheon.config; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; import io.vertx.core.json.JsonObject; public class CliqueConfigOptions { @@ -34,4 +37,9 @@ public long getEpochLength() { public int getBlockPeriodSeconds() { return cliqueConfigRoot.getInteger("blockperiodseconds", DEFAULT_BLOCK_PERIOD_SECONDS); } + + Map asMap() { + return ImmutableMap.of( + "epochLength", getEpochLength(), "blockPeriodSeconds", getBlockPeriodSeconds()); + } } diff --git a/config/src/main/java/tech/pegasys/pantheon/config/EthashConfigOptions.java b/config/src/main/java/tech/pegasys/pantheon/config/EthashConfigOptions.java index f861c17e58..2cb7e81e1f 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/EthashConfigOptions.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/EthashConfigOptions.java @@ -12,8 +12,10 @@ */ package tech.pegasys.pantheon.config; +import java.util.Map; import java.util.OptionalLong; +import com.google.common.collect.ImmutableMap; import io.vertx.core.json.JsonObject; public class EthashConfigOptions { @@ -28,4 +30,10 @@ public class EthashConfigOptions { public OptionalLong getFixedDifficulty() { return ConfigUtil.getOptionalLong(ethashConfigRoot, "fixeddifficulty"); } + + Map asMap() { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + getFixedDifficulty().ifPresent(l -> builder.put("fixeddifficulty", l)); + return builder.build(); + } } diff --git a/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java b/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java index 8d9673c16f..4312c4352b 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/GenesisConfigOptions.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon.config; +import java.util.Map; import java.util.OptionalInt; import java.util.OptionalLong; @@ -48,4 +49,6 @@ public interface GenesisConfigOptions { OptionalLong getConstantinopleFixBlockNumber(); OptionalInt getChainId(); + + Map asMap(); } diff --git a/config/src/main/java/tech/pegasys/pantheon/config/IbftConfigOptions.java b/config/src/main/java/tech/pegasys/pantheon/config/IbftConfigOptions.java index aff38c1507..fa2b083acf 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/IbftConfigOptions.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/IbftConfigOptions.java @@ -12,6 +12,9 @@ */ package tech.pegasys.pantheon.config; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; import io.vertx.core.json.JsonObject; public class IbftConfigOptions { @@ -67,4 +70,33 @@ public int getFutureMessagesMaxDistance() { return ibftConfigRoot.getInteger( "futuremessagesmaxdistance", DEFAULT_FUTURE_MESSAGES_MAX_DISTANCE); } + + Map asMap() { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + if (ibftConfigRoot.containsKey("epochlength")) { + builder.put("epochLength", getEpochLength()); + } + if (ibftConfigRoot.containsKey("blockperiodseconds")) { + builder.put("blockPeriodSeconds", getBlockPeriodSeconds()); + } + if (ibftConfigRoot.containsKey("requesttimeoutseconds")) { + builder.put("requestTimeoutSeconds", getRequestTimeoutSeconds()); + } + if (ibftConfigRoot.containsKey("gossipedhistorylimit")) { + builder.put("gossipedHistoryLimit", getGossipedHistoryLimit()); + } + if (ibftConfigRoot.containsKey("messagequeuelimit")) { + builder.put("messageQueueLimit", getMessageQueueLimit()); + } + if (ibftConfigRoot.containsKey("duplicatemessagelimit")) { + builder.put("duplicateMessageLimit", getDuplicateMessageLimit()); + } + if (ibftConfigRoot.containsKey("futuremessageslimit")) { + builder.put("futureMessagesLimit", getFutureMessagesLimit()); + } + if (ibftConfigRoot.containsKey("futuremessagesmaxdistance")) { + builder.put("futureMessagesMaxDistance", getFutureMessagesMaxDistance()); + } + return builder.build(); + } } diff --git a/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java b/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java index 18ee4ad291..256cf624d7 100644 --- a/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/tech/pegasys/pantheon/config/JsonGenesisConfigOptions.java @@ -12,9 +12,11 @@ */ package tech.pegasys.pantheon.config; +import java.util.Map; import java.util.OptionalInt; import java.util.OptionalLong; +import com.google.common.collect.ImmutableMap; import io.vertx.core.json.JsonObject; public class JsonGenesisConfigOptions implements GenesisConfigOptions { @@ -119,6 +121,49 @@ public OptionalInt getChainId() { : OptionalInt.empty(); } + @Override + public Map asMap() { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.put("chainId", getChainId().getAsInt()); + getHomesteadBlockNumber().ifPresent(l -> builder.put("homesteadBlock", l)); + getDaoForkBlock() + .ifPresent( + l -> { + builder.put("daoForkBlock", l); + builder.put("daoForkSupport", Boolean.TRUE); + }); + getTangerineWhistleBlockNumber() + .ifPresent( + l -> { + builder.put("eip150Block", l); + if (configRoot.containsKey("eip150hash")) { + builder.put("eip150Hash", configRoot.getString("eip150hash")); + } + }); + getSpuriousDragonBlockNumber() + .ifPresent( + l -> { + builder.put("eip155Block", l); + builder.put("eip158Block", l); + }); + getByzantiumBlockNumber().ifPresent(l -> builder.put("byzantiumBlock", l)); + getConstantinopleBlockNumber().ifPresent(l -> builder.put("constantinopleBlock", l)); + getConstantinopleFixBlockNumber().ifPresent(l -> builder.put("constantinopleFixBlock", l)); + if (isClique()) { + builder.put("clique", getCliqueConfigOptions().asMap()); + } + if (isEthHash()) { + builder.put("ethash", getEthashConfigOptions().asMap()); + } + if (isIbftLegacy()) { + builder.put("ibft", getIbftLegacyConfigOptions().asMap()); + } + if (isIbft2()) { + builder.put("ibft2", getIbft2ConfigOptions().asMap()); + } + return builder.build(); + } + private OptionalLong getOptionalLong(final String key) { return ConfigUtil.getOptionalLong(configRoot, key); } diff --git a/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java b/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java index 6ab5abbf7e..02b925947b 100644 --- a/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java +++ b/config/src/test-support/java/tech/pegasys/pantheon/config/StubGenesisConfigOptions.java @@ -12,9 +12,12 @@ */ package tech.pegasys.pantheon.config; +import java.util.Map; import java.util.OptionalInt; import java.util.OptionalLong; +import com.google.common.collect.ImmutableMap; + public class StubGenesisConfigOptions implements GenesisConfigOptions { private OptionalLong homesteadBlockNumber = OptionalLong.empty(); @@ -106,6 +109,42 @@ public OptionalInt getChainId() { return chainId; } + @Override + public Map asMap() { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.put("chainId", getChainId().getAsInt()); + getHomesteadBlockNumber().ifPresent(l -> builder.put("homesteadBlock", l)); + getDaoForkBlock() + .ifPresent( + l -> { + builder.put("daoForkBlock", l); + builder.put("daoForkSupport", Boolean.TRUE); + }); + getTangerineWhistleBlockNumber().ifPresent(l -> builder.put("eip150Block", l)); + getSpuriousDragonBlockNumber() + .ifPresent( + l -> { + builder.put("eip155Block", l); + builder.put("eip158Block", l); + }); + getByzantiumBlockNumber().ifPresent(l -> builder.put("byzantiumBlock", l)); + getConstantinopleBlockNumber().ifPresent(l -> builder.put("constantinopleBlock", l)); + getConstantinopleFixBlockNumber().ifPresent(l -> builder.put("constantinopleFixBlock", l)); + if (isClique()) { + builder.put("clique", getCliqueConfigOptions().asMap()); + } + if (isEthHash()) { + builder.put("ethash", getEthashConfigOptions().asMap()); + } + if (isIbftLegacy()) { + builder.put("ibft", getIbftLegacyConfigOptions().asMap()); + } + if (isIbft2()) { + builder.put("ibft2", getIbft2ConfigOptions().asMap()); + } + return builder.build(); + } + public StubGenesisConfigOptions homesteadBlock(final long blockNumber) { homesteadBlockNumber = OptionalLong.of(blockNumber); return this; diff --git a/ethereum/jsonrpc/build.gradle b/ethereum/jsonrpc/build.gradle index 890828a239..5824456d83 100644 --- a/ethereum/jsonrpc/build.gradle +++ b/ethereum/jsonrpc/build.gradle @@ -26,6 +26,7 @@ jar { } dependencies { + implementation project(':config') implementation project(':crypto') implementation project(':enclave') implementation project(':ethereum:blockcreation') diff --git a/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java b/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java index 039de2b68c..cf331a51f7 100644 --- a/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java +++ b/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java @@ -16,6 +16,7 @@ import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain; import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; +import tech.pegasys.pantheon.config.StubGenesisConfigOptions; import tech.pegasys.pantheon.ethereum.ProtocolContext; import tech.pegasys.pantheon.ethereum.blockcreation.EthHashMiningCoordinator; import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain; @@ -47,6 +48,7 @@ public class JsonRpcTestMethodsFactory { private static final String CLIENT_VERSION = "TestClientVersion/0.1.0"; + private static final int NETWORK_ID = 123; private final BlockchainImporter importer; @@ -87,6 +89,8 @@ public Map methods() { return new JsonRpcMethodsFactory() .methods( CLIENT_VERSION, + NETWORK_ID, + new StubGenesisConfigOptions(), peerDiscovery, blockchainQueries, synchronizer, diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java index 44aa7fe9da..54ca4670a2 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon.ethereum.jsonrpc; +import tech.pegasys.pantheon.config.GenesisConfigOptions; import tech.pegasys.pantheon.enclave.Enclave; import tech.pegasys.pantheon.ethereum.blockcreation.MiningCoordinator; import tech.pegasys.pantheon.ethereum.chain.Blockchain; @@ -20,6 +21,7 @@ import tech.pegasys.pantheon.ethereum.core.TransactionPool; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.filter.FilterManager; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.AdminAddPeer; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.AdminNodeInfo; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.AdminPeers; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.DebugMetrics; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.DebugStorageRangeAt; @@ -106,6 +108,8 @@ public class JsonRpcMethodsFactory { public Map methods( final String clientVersion, + final int networkId, + final GenesisConfigOptions genesisConfigOptions, final P2PNetwork peerNetworkingService, final Blockchain blockchain, final WorldStateArchive worldStateArchive, @@ -123,6 +127,8 @@ public Map methods( new BlockchainQueries(blockchain, worldStateArchive); return methods( clientVersion, + networkId, + genesisConfigOptions, peerNetworkingService, blockchainQueries, synchronizer, @@ -139,6 +145,8 @@ public Map methods( public Map methods( final String clientVersion, + final int networkId, + final GenesisConfigOptions genesisConfigOptions, final P2PNetwork p2pNetwork, final BlockchainQueries blockchainQueries, final Synchronizer synchronizer, @@ -252,8 +260,12 @@ blockchainQueries, new TransactionTracer(blockReplay), parameter), accountsWhitelistController, p2pNetwork.getNodeWhitelistController())); } if (rpcApis.contains(RpcApis.ADMIN)) { - addMethods(enabledMethods, new AdminPeers(p2pNetwork)); - addMethods(enabledMethods, new AdminAddPeer(p2pNetwork, parameter)); + addMethods( + enabledMethods, + new AdminAddPeer(p2pNetwork, parameter), + new AdminNodeInfo( + clientVersion, networkId, genesisConfigOptions, p2pNetwork, blockchainQueries), + new AdminPeers(p2pNetwork)); } if (rpcApis.contains(RpcApis.EEA)) { addMethods( diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminNodeInfo.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminNodeInfo.java new file mode 100644 index 0000000000..301ba30b8e --- /dev/null +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminNodeInfo.java @@ -0,0 +1,115 @@ +/* + * 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.ethereum.jsonrpc.internal.methods; + +import tech.pegasys.pantheon.config.GenesisConfigOptions; +import tech.pegasys.pantheon.ethereum.chain.ChainHead; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; +import tech.pegasys.pantheon.ethereum.p2p.P2pDisabledException; +import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.util.bytes.BytesValue; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.google.common.net.InetAddresses; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class AdminNodeInfo implements JsonRpcMethod { + + private static final Logger LOG = LogManager.getLogger(); + + private final String clientVersion; + private final int networkId; + private final GenesisConfigOptions genesisConfigOptions; + private final P2PNetwork peerNetwork; + private final BlockchainQueries blockchainQueries; + + public AdminNodeInfo( + final String clientVersion, + final int networkId, + final GenesisConfigOptions genesisConfigOptions, + final P2PNetwork peerNetwork, + final BlockchainQueries blockchainQueries) { + this.peerNetwork = peerNetwork; + this.clientVersion = clientVersion; + this.genesisConfigOptions = genesisConfigOptions; + this.blockchainQueries = blockchainQueries; + this.networkId = networkId; + } + + @Override + public String getName() { + return "admin_nodeInfo"; + } + + @Override + public JsonRpcResponse response(final JsonRpcRequest req) { + + try { + final Map response = new HashMap<>(); + final BytesValue nodeId = peerNetwork.getLocalPeerInfo().getNodeId(); + final InetSocketAddress address = peerNetwork.getDiscoverySocketAddress(); + final int port = peerNetwork.getLocalPeerInfo().getPort(); + + final InetAddress inetAddress = address.getAddress(); + response.put( + "enode", + "enode://" + + nodeId.toString().substring(2) + + "@" + + InetAddresses.toUriString(inetAddress) + + ":" + + port); + response.put("id", nodeId.toString().substring(2)); + // this doesn't provide a useful value yet. + // response.put("ip", inetAddress.getHostAddress()); + response.put("listenAddr", InetAddresses.toUriString(inetAddress) + ":" + port); + response.put("name", clientVersion); + response.put("ports", ImmutableMap.of("discovery", port, "listener", port /*??*/)); + + final ChainHead chainHead = blockchainQueries.getBlockchain().getChainHead(); + response.put( + "protocols", + ImmutableMap.of( + "eth", + ImmutableMap.of( + "config", + genesisConfigOptions.asMap(), + "difficulty", + chainHead.getTotalDifficulty().toLong(), + "genesis", + blockchainQueries.getBlockHashByNumber(0).get().toString(), + "head", + chainHead.getHash().toString(), + "network", + networkId))); + + return new JsonRpcSuccessResponse(req.getId(), response); + } catch (final P2pDisabledException e) { + return new JsonRpcErrorResponse(req.getId(), JsonRpcError.P2P_DISABLED); + } catch (final Exception e) { + LOG.error("Error processing request: " + req, e); + throw e; + } + } +} diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java index ff364b577a..37264f01fc 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java @@ -19,6 +19,7 @@ import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain; import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; +import tech.pegasys.pantheon.config.StubGenesisConfigOptions; import tech.pegasys.pantheon.ethereum.ProtocolContext; import tech.pegasys.pantheon.ethereum.blockcreation.EthHashMiningCoordinator; import tech.pegasys.pantheon.ethereum.chain.GenesisState; @@ -94,6 +95,8 @@ public abstract class AbstractEthJsonRpcHttpServiceTest { protected final String CLIENT_VERSION = "TestClientVersion/0.1.0"; + protected final int NETWORK_ID = 123; + protected static final Collection JSON_RPC_APIS = Arrays.asList(RpcApis.ETH, RpcApis.NET, RpcApis.WEB3); @@ -171,6 +174,8 @@ public void setupTest() throws Exception { new JsonRpcMethodsFactory() .methods( CLIENT_VERSION, + NETWORK_ID, + new StubGenesisConfigOptions(), peerDiscoveryMock, blockchainQueries, synchronizerMock, diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java index 3d072dd287..26c2e3752a 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java @@ -86,6 +86,8 @@ public void initServerAndClient() throws Exception { new JsonRpcMethodsFactory() .methods( CLIENT_VERSION, + CHAIN_ID, + new StubGenesisConfigOptions(), peerDiscoveryMock, blockchainQueries, synchronizer, diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java index 90622a5f1e..abd3d94b8e 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java @@ -111,17 +111,20 @@ public static void initServerAndClient() throws Exception { supportedCapabilities.add(EthProtocol.ETH62); supportedCapabilities.add(EthProtocol.ETH63); + final StubGenesisConfigOptions genesisConfigOptions = + new StubGenesisConfigOptions().constantinopleBlock(0).chainId(CHAIN_ID); rpcMethods = spy( new JsonRpcMethodsFactory() .methods( CLIENT_VERSION, + CHAIN_ID, + genesisConfigOptions, peerDiscoveryMock, blockchainQueries, synchronizer, MainnetProtocolSchedule.fromConfig( - new StubGenesisConfigOptions().constantinopleBlock(0).chainId(CHAIN_ID), - PrivacyParameters.noPrivacy()), + genesisConfigOptions, PrivacyParameters.noPrivacy()), mock(FilterManager.class), mock(TransactionPool.class), mock(EthHashMiningCoordinator.class), diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java index ce17d4959c..eb10d8e40e 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java @@ -17,6 +17,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import tech.pegasys.pantheon.config.StubGenesisConfigOptions; import tech.pegasys.pantheon.ethereum.blockcreation.EthHashMiningCoordinator; import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; import tech.pegasys.pantheon.ethereum.core.Synchronizer; @@ -65,6 +66,7 @@ public class JsonRpcHttpServiceRpcApisTest { private static String baseUrl; private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); private static final String CLIENT_VERSION = "TestClientVersion/0.1.0"; + private static final int NETWORK_ID = 123; private JsonRpcConfiguration configuration; @Mock protected static BlockchainQueries blockchainQueries; @@ -169,6 +171,8 @@ private JsonRpcHttpService createJsonRpcHttpServiceWithRpcApis(final JsonRpcConf new JsonRpcMethodsFactory() .methods( CLIENT_VERSION, + NETWORK_ID, + new StubGenesisConfigOptions(), mock(P2PNetwork.class), blockchainQueries, mock(Synchronizer.class), diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java index 6a9e3bbacf..b4d7431c5e 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java @@ -114,6 +114,8 @@ public static void initServerAndClient() throws Exception { new JsonRpcMethodsFactory() .methods( CLIENT_VERSION, + CHAIN_ID, + new StubGenesisConfigOptions(), peerDiscoveryMock, blockchainQueries, synchronizer, diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminNodeInfoTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminNodeInfoTest.java new file mode 100644 index 0000000000..0471d1b42a --- /dev/null +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminNodeInfoTest.java @@ -0,0 +1,107 @@ +/* + * 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.ethereum.jsonrpc.internal.methods; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +import tech.pegasys.pantheon.config.GenesisConfigOptions; +import tech.pegasys.pantheon.config.StubGenesisConfigOptions; +import tech.pegasys.pantheon.ethereum.chain.Blockchain; +import tech.pegasys.pantheon.ethereum.chain.ChainHead; +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; +import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.wire.PeerInfo; +import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.uint.UInt256; + +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import com.google.common.collect.ImmutableMap; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.StrictStubs.class) +public class AdminNodeInfoTest { + + @Mock private P2PNetwork p2pNetwork; + @Mock private Blockchain blockchain; + @Mock private BlockchainQueries blockchainQueries; + + private AdminNodeInfo method; + + private final PeerInfo localPeer = + new PeerInfo(5, "0x0", Collections.emptyList(), 30303, BytesValue.EMPTY); + private final InetSocketAddress discoverySocketAddress = new InetSocketAddress("1.2.3.4", 7890); + private final ChainHead testChainHead = new ChainHead(Hash.EMPTY, UInt256.ONE); + private final GenesisConfigOptions genesisConfigOptions = + new StubGenesisConfigOptions().chainId(2019); + + @Before + public void setup() { + when(p2pNetwork.getLocalPeerInfo()).thenReturn(localPeer); + when(p2pNetwork.getDiscoverySocketAddress()).thenReturn(discoverySocketAddress); + when(blockchainQueries.getBlockchain()).thenReturn(blockchain); + when(blockchainQueries.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(Hash.EMPTY)); + when(blockchain.getChainHead()).thenReturn(testChainHead); + + method = + new AdminNodeInfo( + "testnet/1.0/this/that", 2018, genesisConfigOptions, p2pNetwork, blockchainQueries); + } + + @Test + public void shouldReturnCorrectResult() { + final JsonRpcRequest request = adminNodeInfo(); + + final JsonRpcSuccessResponse actual = (JsonRpcSuccessResponse) method.response(request); + final Map expected = new HashMap<>(); + expected.put("enode", "enode://@1.2.3.4:30303"); + expected.put("id", ""); + expected.put("listenAddr", "1.2.3.4:30303"); + expected.put("name", "testnet/1.0/this/that"); + expected.put("ports", ImmutableMap.of("discovery", 30303, "listener", 30303)); + expected.put( + "protocols", + ImmutableMap.of( + "eth", + ImmutableMap.of( + "config", + genesisConfigOptions.asMap(), + "difficulty", + 1L, + "genesis", + Hash.EMPTY.toString(), + "head", + Hash.EMPTY.toString(), + "network", + 2018))); + + assertThat(actual.getResult()).isEqualTo(expected); + } + + private JsonRpcRequest adminNodeInfo() { + return new JsonRpcRequest("2.0", "admin_nodeInfo", new Object[] {}); + } +} diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 36b92dccf6..b2eedfb756 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon; +import tech.pegasys.pantheon.cli.EthNetworkConfig; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.ProtocolContext; @@ -78,7 +79,7 @@ public class RunnerBuilder { private PantheonController pantheonController; private boolean p2pEnabled = true; private boolean discovery; - private Collection bootstrapPeers; + private EthNetworkConfig ethNetworkConfig; private String discoveryHost; private int listenPort; private int maxPeers; @@ -110,8 +111,8 @@ public RunnerBuilder discovery(final boolean discovery) { return this; } - public RunnerBuilder bootstrapPeers(final Collection bootstrapPeers) { - this.bootstrapPeers = bootstrapPeers; + public RunnerBuilder ethNetworkConfig(final EthNetworkConfig ethNetworkConfig) { + this.ethNetworkConfig = ethNetworkConfig; return this; } @@ -173,10 +174,10 @@ public Runner build() { final DiscoveryConfiguration discoveryConfiguration; if (discovery) { final Collection bootstrap; - if (bootstrapPeers == null) { + if (ethNetworkConfig.getBootNodes() == null) { bootstrap = DiscoveryConfiguration.MAINNET_BOOTSTRAP_NODES; } else { - bootstrap = bootstrapPeers; + bootstrap = ethNetworkConfig.getBootNodes(); } discoveryConfiguration = DiscoveryConfiguration.create() @@ -251,7 +252,7 @@ public Runner build() { .filter(PermissioningConfiguration::isAccountWhitelistEnabled) .map( configuration -> { - AccountWhitelistController whitelistController = + final AccountWhitelistController whitelistController = new AccountWhitelistController(configuration); transactionPool.setAccountWhitelist(whitelistController); return whitelistController; @@ -363,6 +364,8 @@ private Map jsonRpcMethods( new JsonRpcMethodsFactory() .methods( PantheonInfo.version(), + ethNetworkConfig.getNetworkId(), + pantheonController.getGenesisConfigOptions(), network, context.getBlockchain(), context.getWorldStateArchive(), diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/EthNetworkConfig.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/EthNetworkConfig.java index 9f4e05aaa7..ae125e95dd 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/EthNetworkConfig.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/EthNetworkConfig.java @@ -30,14 +30,14 @@ import com.google.common.io.Resources; public class EthNetworkConfig { - private static final int MAINNET_NETWORK_ID = 1; - private static final int RINKEBY_NETWORK_ID = 4; - private static final int ROPSTEN_NETWORK_ID = 3; - private static final int GOERLI_NETWORK_ID = 5; - private static final int DEV_NETWORK_ID = 2018; + public static final int MAINNET_NETWORK_ID = 1; + public static final int ROPSTEN_NETWORK_ID = 3; + public static final int RINKEBY_NETWORK_ID = 4; + public static final int GOERLI_NETWORK_ID = 5; + public static final int DEV_NETWORK_ID = 2018; private static final String MAINNET_GENESIS = "mainnet.json"; - private static final String RINKEBY_GENESIS = "rinkeby.json"; private static final String ROPSTEN_GENESIS = "ropsten.json"; + private static final String RINKEBY_GENESIS = "rinkeby.json"; private static final String GOERLI_GENESIS = "goerli.json"; private static final String DEV_GENESIS = "dev.json"; private final String genesisConfig; @@ -118,13 +118,30 @@ public static EthNetworkConfig getNetworkConfig(final NetworkName networkName) { private static String jsonConfig(final String resourceName) { try { - URI uri = Resources.getResource(resourceName).toURI(); + final URI uri = Resources.getResource(resourceName).toURI(); return Resources.toString(uri.toURL(), UTF_8); } catch (final URISyntaxException | IOException e) { throw new IllegalStateException(e); } } + public static String jsonConfig(final NetworkName network) { + switch (network) { + case MAINNET: + return jsonConfig(MAINNET_GENESIS); + case ROPSTEN: + return jsonConfig(ROPSTEN_GENESIS); + case RINKEBY: + return jsonConfig(RINKEBY_GENESIS); + case GOERLI: + return jsonConfig(GOERLI_GENESIS); + case DEV: + return jsonConfig(DEV_GENESIS); + default: + throw new IllegalArgumentException("Unknown network:" + network); + } + } + public static class Builder { private String genesisConfig; 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 9060fe7a7b..a8a71514ea 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -215,6 +215,7 @@ public static class RpcApisConversionException extends Exception { + " (default: MAINNET)") private final NetworkName network = null; + @SuppressWarnings("FieldMayBeFinal") // Because PicoCLI requires Strings to not be final. @Option( names = {"--p2p-host"}, paramLabel = MANDATORY_HOST_FORMAT_HELP, @@ -242,6 +243,7 @@ public static class RpcApisConversionException extends Exception { description = "Set to start the JSON-RPC HTTP service (default: ${DEFAULT-VALUE})") private final Boolean isRpcHttpEnabled = false; + @SuppressWarnings("FieldMayBeFinal") // Because PicoCLI requires Strings to not be final. @Option( names = {"--rpc-http-host"}, paramLabel = MANDATORY_HOST_FORMAT_HELP, @@ -284,6 +286,7 @@ public static class RpcApisConversionException extends Exception { description = "Set to start the JSON-RPC WebSocket service (default: ${DEFAULT-VALUE})") private final Boolean isRpcWsEnabled = false; + @SuppressWarnings("FieldMayBeFinal") // Because PicoCLI requires Strings to not be final. @Option( names = {"--rpc-ws-host"}, paramLabel = MANDATORY_HOST_FORMAT_HELP, @@ -321,6 +324,7 @@ public static class RpcApisConversionException extends Exception { description = "Set to start the metrics exporter (default: ${DEFAULT-VALUE})") private final Boolean isMetricsEnabled = false; + @SuppressWarnings("FieldMayBeFinal") // Because PicoCLI requires Strings to not be final. @Option( names = {"--metrics-host"}, paramLabel = MANDATORY_HOST_FORMAT_HELP, @@ -349,6 +353,7 @@ public static class RpcApisConversionException extends Exception { description = "Enable the metrics push gateway integration (default: ${DEFAULT-VALUE})") private final Boolean isMetricsPushEnabled = false; + @SuppressWarnings("FieldMayBeFinal") // Because PicoCLI requires Strings to not be final. @Option( names = {"--metrics-push-host"}, paramLabel = MANDATORY_HOST_FORMAT_HELP, @@ -371,6 +376,7 @@ public static class RpcApisConversionException extends Exception { arity = "1") private final Integer metricsPushInterval = 15; + @SuppressWarnings("FieldMayBeFinal") // Because PicoCLI requires Strings to not be final. @Option( names = {"--metrics-push-prometheus-job"}, description = "Job name to use when in push mode (default: ${DEFAULT-VALUE})", @@ -593,7 +599,7 @@ public void run() { buildController(), p2pEnabled, peerDiscoveryEnabled, - ethNetworkConfig.getBootNodes(), + ethNetworkConfig, maxPeers, p2pHost, p2pPort, @@ -601,7 +607,7 @@ public void run() { webSocketConfiguration, metricsConfiguration(), permissioningConfiguration); - } catch (Exception e) { + } catch (final Exception e) { throw new ParameterException(this.commandLine, e.getMessage(), e); } } @@ -652,7 +658,7 @@ private String getPermissionsConfigFile() { + DefaultCommandValues.PERMISSIONING_CONFIG_LOCATION; } - private JsonRpcConfiguration jsonRpcConfiguration() throws Exception { + private JsonRpcConfiguration jsonRpcConfiguration() { CommandLineUtils.checkOptionDependencies( logger, @@ -810,7 +816,7 @@ private void synchronize( final PantheonController controller, final boolean p2pEnabled, final boolean peerDiscoveryEnabled, - final Collection bootstrapNodes, + final EthNetworkConfig ethNetworkConfig, final int maxPeers, final String discoveryHost, final int discoveryPort, @@ -829,7 +835,7 @@ private void synchronize( .pantheonController(controller) .p2pEnabled(p2pEnabled) .discovery(peerDiscoveryEnabled) - .bootstrapPeers(bootstrapNodes) + .ethNetworkConfig(ethNetworkConfig) .discoveryHost(discoveryHost) .discoveryPort(discoveryPort) .maxPeers(maxPeers) @@ -996,7 +1002,7 @@ private File privacyPublicKeyFile() { } } - private String rpcHttpAuthenticationCredentialsFile() throws Exception { + private String rpcHttpAuthenticationCredentialsFile() { String filename = null; if (isFullInstantiation()) { filename = standaloneCommands.rpcHttpAuthenticationCredentialsFile; diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonController.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonController.java index a608d0d592..8a32e4dc08 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonController.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/CliquePantheonController.java @@ -16,6 +16,7 @@ import tech.pegasys.pantheon.config.CliqueConfigOptions; import tech.pegasys.pantheon.config.GenesisConfigFile; +import tech.pegasys.pantheon.config.GenesisConfigOptions; import tech.pegasys.pantheon.consensus.clique.CliqueBlockInterface; import tech.pegasys.pantheon.consensus.clique.CliqueContext; import tech.pegasys.pantheon.consensus.clique.CliqueMiningTracker; @@ -70,6 +71,7 @@ public class CliquePantheonController implements PantheonController protocolSchedule; private final ProtocolContext context; + private final GenesisConfigOptions genesisConfigOptions; private final Synchronizer synchronizer; private final ProtocolManager ethProtocolManager; private final KeyPair keyPair; @@ -78,9 +80,10 @@ public class CliquePantheonController implements PantheonController protocolSchedule, final ProtocolContext context, + final GenesisConfigOptions genesisConfigOptions, final ProtocolManager ethProtocolManager, final Synchronizer synchronizer, final KeyPair keyPair, @@ -90,6 +93,7 @@ public class CliquePantheonController implements PantheonController init( + static PantheonController init( final StorageProvider storageProvider, final GenesisConfigFile genesisConfig, final SynchronizerConfiguration syncConfig, @@ -192,6 +196,7 @@ public static PantheonController init( return new CliquePantheonController( protocolSchedule, protocolContext, + genesisConfig.getConfigOptions(), ethProtocolManager, synchronizer, nodeKeys, @@ -223,6 +228,11 @@ public ProtocolSchedule getProtocolSchedule() { return protocolSchedule; } + @Override + public GenesisConfigOptions getGenesisConfigOptions() { + return genesisConfigOptions; + } + @Override public Synchronizer getSynchronizer() { return synchronizer; diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftLegacyPantheonController.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftLegacyPantheonController.java index d40fbf5fd0..569ce3ef61 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftLegacyPantheonController.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftLegacyPantheonController.java @@ -15,6 +15,7 @@ import static org.apache.logging.log4j.LogManager.getLogger; import tech.pegasys.pantheon.config.GenesisConfigFile; +import tech.pegasys.pantheon.config.GenesisConfigOptions; import tech.pegasys.pantheon.config.IbftConfigOptions; import tech.pegasys.pantheon.consensus.common.EpochManager; import tech.pegasys.pantheon.consensus.common.VoteProposer; @@ -62,6 +63,7 @@ public class IbftLegacyPantheonController implements PantheonController protocolSchedule; private final ProtocolContext context; + private final GenesisConfigOptions genesisConfigOptions; private final Synchronizer synchronizer; private final SubProtocol ethSubProtocol; private final ProtocolManager ethProtocolManager; @@ -69,9 +71,10 @@ public class IbftLegacyPantheonController implements PantheonController protocolSchedule, final ProtocolContext context, + final GenesisConfigOptions genesisConfigOptions, final SubProtocol ethSubProtocol, final ProtocolManager ethProtocolManager, final Synchronizer synchronizer, @@ -81,6 +84,7 @@ public class IbftLegacyPantheonController implements PantheonController init( + static PantheonController init( final StorageProvider storageProvider, final GenesisConfigFile genesisConfig, final SynchronizerConfiguration syncConfig, @@ -183,6 +187,7 @@ public static PantheonController init( return new IbftLegacyPantheonController( protocolSchedule, protocolContext, + genesisConfig.getConfigOptions(), ethSubProtocol, ethProtocolManager, synchronizer, @@ -201,6 +206,11 @@ public ProtocolSchedule getProtocolSchedule() { return protocolSchedule; } + @Override + public GenesisConfigOptions getGenesisConfigOptions() { + return genesisConfigOptions; + } + @Override public Synchronizer getSynchronizer() { return synchronizer; diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java index bc87db583e..15ddbf17ec 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java @@ -16,6 +16,7 @@ import static tech.pegasys.pantheon.ethereum.eth.manager.MonitoredExecutors.newScheduledThreadPool; import tech.pegasys.pantheon.config.GenesisConfigFile; +import tech.pegasys.pantheon.config.GenesisConfigOptions; import tech.pegasys.pantheon.config.IbftConfigOptions; import tech.pegasys.pantheon.consensus.common.BlockInterface; import tech.pegasys.pantheon.consensus.common.EpochManager; @@ -94,18 +95,20 @@ public class IbftPantheonController implements PantheonController { private static final Logger LOG = getLogger(); private final ProtocolSchedule protocolSchedule; private final ProtocolContext context; + private final GenesisConfigOptions genesisConfigOptions; private final Synchronizer synchronizer; private final SubProtocol ethSubProtocol; private final ProtocolManager ethProtocolManager; private final IbftProtocolManager ibftProtocolManager; private final KeyPair keyPair; private final TransactionPool transactionPool; - private MiningCoordinator ibftMiningCoordinator; + private final MiningCoordinator ibftMiningCoordinator; private final Runnable closer; - IbftPantheonController( + private IbftPantheonController( final ProtocolSchedule protocolSchedule, final ProtocolContext context, + final GenesisConfigOptions genesisConfigOptions, final SubProtocol ethSubProtocol, final ProtocolManager ethProtocolManager, final IbftProtocolManager ibftProtocolManager, @@ -116,6 +119,7 @@ public class IbftPantheonController implements PantheonController { final Runnable closer) { this.protocolSchedule = protocolSchedule; this.context = context; + this.genesisConfigOptions = genesisConfigOptions; this.ethSubProtocol = ethSubProtocol; this.ethProtocolManager = ethProtocolManager; this.ibftProtocolManager = ibftProtocolManager; @@ -126,7 +130,7 @@ public class IbftPantheonController implements PantheonController { this.closer = closer; } - public static PantheonController init( + static PantheonController init( final StorageProvider storageProvider, final GenesisConfigFile genesisConfig, final SynchronizerConfiguration syncConfig, @@ -303,6 +307,7 @@ public static PantheonController init( return new IbftPantheonController( protocolSchedule, protocolContext, + genesisConfig.getConfigOptions(), ethSubProtocol, ethProtocolManager, new IbftProtocolManager(ibftEventQueue, peers), @@ -323,6 +328,11 @@ public ProtocolSchedule getProtocolSchedule() { return protocolSchedule; } + @Override + public GenesisConfigOptions getGenesisConfigOptions() { + return genesisConfigOptions; + } + @Override public Synchronizer getSynchronizer() { return synchronizer; diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/MainnetPantheonController.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/MainnetPantheonController.java index ce4e9512ac..96458f5191 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/MainnetPantheonController.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/MainnetPantheonController.java @@ -13,6 +13,7 @@ package tech.pegasys.pantheon.controller; import tech.pegasys.pantheon.config.GenesisConfigFile; +import tech.pegasys.pantheon.config.GenesisConfigOptions; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.ProtocolContext; import tech.pegasys.pantheon.ethereum.blockcreation.DefaultBlockScheduler; @@ -59,6 +60,7 @@ public class MainnetPantheonController implements PantheonController { private final ProtocolSchedule protocolSchedule; private final ProtocolContext protocolContext; + private final GenesisConfigOptions genesisConfigOptions; private final ProtocolManager ethProtocolManager; private final KeyPair keyPair; private final Synchronizer synchronizer; @@ -68,9 +70,10 @@ public class MainnetPantheonController implements PantheonController { private final PrivacyParameters privacyParameters; private final Runnable close; - public MainnetPantheonController( + private MainnetPantheonController( final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, + final GenesisConfigOptions genesisConfigOptions, final ProtocolManager ethProtocolManager, final Synchronizer synchronizer, final KeyPair keyPair, @@ -80,6 +83,7 @@ public MainnetPantheonController( final Runnable close) { this.protocolSchedule = protocolSchedule; this.protocolContext = protocolContext; + this.genesisConfigOptions = genesisConfigOptions; this.ethProtocolManager = ethProtocolManager; this.synchronizer = synchronizer; this.keyPair = keyPair; @@ -131,11 +135,11 @@ public static PantheonController init( dataDirectory, metricsSystem); - OptionalLong daoBlock = genesisConfig.getConfigOptions().getDaoForkBlock(); + final OptionalLong daoBlock = genesisConfig.getConfigOptions().getDaoForkBlock(); if (daoBlock.isPresent()) { // Setup dao validator - EthContext ethContext = ethProtocolManager.ethContext(); - DaoForkPeerValidator daoForkPeerValidator = + final EthContext ethContext = ethProtocolManager.ethContext(); + final DaoForkPeerValidator daoForkPeerValidator = new DaoForkPeerValidator( ethContext, protocolSchedule, metricsSystem, daoBlock.getAsLong()); PeerValidatorRunner.runValidator(ethContext, daoForkPeerValidator); @@ -168,6 +172,7 @@ public static PantheonController init( return new MainnetPantheonController( protocolSchedule, protocolContext, + genesisConfig.getConfigOptions(), ethProtocolManager, synchronizer, nodeKeys, @@ -200,6 +205,11 @@ public ProtocolSchedule getProtocolSchedule() { return protocolSchedule; } + @Override + public GenesisConfigOptions getGenesisConfigOptions() { + return genesisConfigOptions; + } + @Override public Synchronizer getSynchronizer() { return synchronizer; diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonController.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonController.java index dfe565efde..2e85c2bf1d 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonController.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/PantheonController.java @@ -106,6 +106,8 @@ static PantheonController fromConfig( ProtocolSchedule getProtocolSchedule(); + GenesisConfigOptions getGenesisConfigOptions(); + Synchronizer getSynchronizer(); SubProtocolConfiguration subProtocolConfiguration(); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index 53eeec1e3d..0cdb477f62 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -13,8 +13,11 @@ package tech.pegasys.pantheon; import static org.assertj.core.api.Assertions.assertThat; +import static tech.pegasys.pantheon.cli.EthNetworkConfig.DEV_NETWORK_ID; +import static tech.pegasys.pantheon.cli.NetworkName.DEV; import static tech.pegasys.pantheon.controller.KeyPairUtil.loadKeyPair; +import tech.pegasys.pantheon.cli.EthNetworkConfig; import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.controller.MainnetPantheonController; import tech.pegasys.pantheon.controller.PantheonController; @@ -144,7 +147,7 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { final Runner runnerAhead = runnerBuilder .pantheonController(controllerAhead) - .bootstrapPeers(Collections.emptyList()) + .ethNetworkConfig(EthNetworkConfig.getNetworkConfig(DEV)) .jsonRpcConfiguration(aheadJsonRpcConfiguration) .webSocketConfiguration(aheadWebSocketConfiguration) .metricsConfiguration(aheadMetricsConfiguration) @@ -189,16 +192,20 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { PrivacyParameters.noPrivacy(), dataDirBehind, noOpMetricsSystem); + final EthNetworkConfig behindEthNetworkConfiguration = + new EthNetworkConfig( + EthNetworkConfig.jsonConfig(DEV), + DEV_NETWORK_ID, + Collections.singletonList( + new DefaultPeer( + aheadDbNodeKeys.getPublicKey().getEncodedBytes(), + listenHost, + runnerAhead.getP2pUdpPort(), + runnerAhead.getP2pTcpPort()))); final Runner runnerBehind = runnerBuilder .pantheonController(controllerBehind) - .bootstrapPeers( - Collections.singletonList( - new DefaultPeer( - aheadDbNodeKeys.getPublicKey().getEncodedBytes(), - listenHost, - runnerAhead.getP2pUdpPort(), - runnerAhead.getP2pTcpPort()))) + .ethNetworkConfig(behindEthNetworkConfiguration) .jsonRpcConfiguration(behindJsonRpcConfiguration) .webSocketConfiguration(behindWebSocketConfiguration) .metricsConfiguration(behindMetricsConfiguration) 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 9c9d107691..f95c707598 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java @@ -33,7 +33,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; -import java.net.URI; import java.nio.file.Path; import java.util.Collection; @@ -78,14 +77,13 @@ public abstract class CommandTestAbstract { @Captor ArgumentCaptor fileArgumentCaptor; @Captor ArgumentCaptor stringArgumentCaptor; @Captor ArgumentCaptor intArgumentCaptor; + @Captor ArgumentCaptor ethNetworkConfigArgumentCaptor; @Captor ArgumentCaptor jsonRpcConfigArgumentCaptor; @Captor ArgumentCaptor wsRpcConfigArgumentCaptor; @Captor ArgumentCaptor metricsConfigArgumentCaptor; @Captor ArgumentCaptor permissioningConfigurationArgumentCaptor; - @Captor ArgumentCaptor> uriListArgumentCaptor; - @Rule public final TemporaryFolder temp = new TemporaryFolder(); @Before @@ -113,7 +111,7 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.vertx(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.pantheonController(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.discovery(anyBoolean())).thenReturn(mockRunnerBuilder); - when(mockRunnerBuilder.bootstrapPeers(any())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.ethNetworkConfig(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.discoveryHost(anyString())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.discoveryPort(anyInt())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.maxPeers(anyInt())).thenReturn(mockRunnerBuilder); 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 a60e5f7a41..6adc8ccfe6 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -55,7 +55,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -99,11 +98,9 @@ public class PantheonCommandTest extends CommandTestAbstract { }; static { - final JsonRpcConfiguration rpcConf = JsonRpcConfiguration.createDefault(); - defaultJsonRpcConfiguration = rpcConf; + defaultJsonRpcConfiguration = JsonRpcConfiguration.createDefault(); - final WebSocketConfiguration websocketConf = WebSocketConfiguration.createDefault(); - defaultWebSocketConfiguration = websocketConf; + defaultWebSocketConfiguration = WebSocketConfiguration.createDefault(); defaultMetricsConfiguration = MetricsConfiguration.createDefault(); } @@ -136,7 +133,12 @@ public void callingPantheonCommandWithoutOptionsMustSyncWithDefaultValues() thro parseCommand(); verify(mockRunnerBuilder).discovery(eq(true)); - verify(mockRunnerBuilder).bootstrapPeers(MAINNET_BOOTSTRAP_NODES); + verify(mockRunnerBuilder) + .ethNetworkConfig( + new EthNetworkConfig( + EthNetworkConfig.jsonConfig(MAINNET), + EthNetworkConfig.MAINNET_NETWORK_ID, + MAINNET_BOOTSTRAP_NODES)); verify(mockRunnerBuilder).discoveryHost(eq("127.0.0.1")); verify(mockRunnerBuilder).discoveryPort(eq(30303)); verify(mockRunnerBuilder).maxPeers(eq(25)); @@ -249,7 +251,7 @@ public void overrideDefaultValuesIfKeyIsPresentInConfigFile() throws IOException .replace("~/genesis.json", escapeTomlString(genesisFile.toString())); final Path toml = createTempFile("toml", updatedConfig.getBytes(UTF_8)); - Collection expectedApis = asList(ETH, WEB3); + final Collection expectedApis = asList(ETH, WEB3); final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault(); jsonRpcConfiguration.setEnabled(false); @@ -272,7 +274,7 @@ public void overrideDefaultValuesIfKeyIsPresentInConfigFile() throws IOException parseCommand("--config-file", toml.toString()); verify(mockRunnerBuilder).discovery(eq(false)); - verify(mockRunnerBuilder).bootstrapPeers(uriListArgumentCaptor.capture()); + verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).discoveryHost(eq("1.2.3.4")); verify(mockRunnerBuilder).discoveryPort(eq(1234)); verify(mockRunnerBuilder).maxPeers(eq(42)); @@ -286,7 +288,7 @@ public void overrideDefaultValuesIfKeyIsPresentInConfigFile() throws IOException URI.create("enode://" + VALID_NODE_ID + "@192.168.0.1:4567"), URI.create("enode://" + VALID_NODE_ID + "@192.168.0.1:4567"), URI.create("enode://" + VALID_NODE_ID + "@192.168.0.1:4567")); - assertThat(uriListArgumentCaptor.getValue()).isEqualTo(nodes); + assertThat(ethNetworkConfigArgumentCaptor.getValue().getBootNodes()).isEqualTo(nodes); final EthNetworkConfig networkConfig = new EthNetworkConfig.Builder(EthNetworkConfig.getNetworkConfig(MAINNET)) @@ -358,11 +360,11 @@ public void permissionsTomlPathMustUseOption() throws IOException { parseCommand( "--permissions-accounts-enabled", "--permissions-config-file", permToml.toString()); - PermissioningConfiguration permissioningConfiguration = + final PermissioningConfiguration permissioningConfiguration = PermissioningConfiguration.createDefault(); permissioningConfiguration.setConfigurationFilePath(permToml.toString()); permissioningConfiguration.setAccountWhitelist( - Arrays.asList("0x0000000000000000000000000000000000000009")); + Collections.singletonList("0x0000000000000000000000000000000000000009")); verify(mockRunnerBuilder) .permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); @@ -429,7 +431,12 @@ public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() throws IOExce final MetricsConfiguration metricsConfiguration = MetricsConfiguration.createDefault(); verify(mockRunnerBuilder).discovery(eq(true)); - verify(mockRunnerBuilder).bootstrapPeers(MAINNET_BOOTSTRAP_NODES); + verify(mockRunnerBuilder) + .ethNetworkConfig( + new EthNetworkConfig( + EthNetworkConfig.jsonConfig(MAINNET), + EthNetworkConfig.MAINNET_NETWORK_ID, + MAINNET_BOOTSTRAP_NODES)); verify(mockRunnerBuilder).discoveryHost(eq("127.0.0.1")); verify(mockRunnerBuilder).discoveryPort(eq(30303)); verify(mockRunnerBuilder).maxPeers(eq(25)); @@ -643,7 +650,7 @@ public void predefinedNetworkIdsMustBeEqualToChainIds() { // id // in this network genesis file. - GenesisConfigFile genesisConfigFile = + final GenesisConfigFile genesisConfigFile = GenesisConfigFile.fromConfig(EthNetworkConfig.getNetworkConfig(MAINNET).getGenesisConfig()); assertThat(genesisConfigFile.getConfigOptions().getChainId().isPresent()).isTrue(); assertThat(genesisConfigFile.getConfigOptions().getChainId().getAsInt()) @@ -742,10 +749,10 @@ public void callingWithBootnodesOptionButNoValueMustDisplayErrorAndUsage() { public void callingWithBootnodesOptionButNoValueMustPassEmptyBootnodeList() { parseCommand("--bootnodes"); - verify(mockRunnerBuilder).bootstrapPeers(uriListArgumentCaptor.capture()); + verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); - assertThat(uriListArgumentCaptor.getValue()).isEmpty(); + assertThat(ethNetworkConfigArgumentCaptor.getValue().getBootNodes()).isEmpty(); assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); @@ -783,10 +790,10 @@ public void callingWithBannedNodeidsOptionButNoValueMustDisplayErrorAndUsage() { public void bootnodesOptionMustBeUsed() { parseCommand("--bootnodes", String.join(",", validENodeStrings)); - verify(mockRunnerBuilder).bootstrapPeers(uriListArgumentCaptor.capture()); + verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); - assertThat(uriListArgumentCaptor.getValue()) + assertThat(ethNetworkConfigArgumentCaptor.getValue().getBootNodes()) .isEqualTo(Stream.of(validENodeStrings).map(URI::create).collect(Collectors.toList())); assertThat(commandOutput.toString()).isEmpty(); @@ -1605,7 +1612,7 @@ public void metricsPrometheusJobMustBeUsed() { } @Test - public void metricsAndMetricsPushMustNotBeUsedTogether() throws Exception { + public void metricsAndMetricsPushMustNotBeUsedTogether() { assumeTrue(isFullInstantiation()); parseCommand("--metrics-enabled", "--metrics-push-enabled"); @@ -1845,7 +1852,7 @@ public void mustUseEnclaveUriAndOptions() throws IOException { } @Test - public void privacyOptionsRequiresServiceToBeEnabled() throws IOException { + public void privacyOptionsRequiresServiceToBeEnabled() { final File file = new File("./specific/public_key"); file.deleteOnExit();