Skip to content

Commit

Permalink
[PAN-3202][BESU-37] Implement EIP-2124 (#156)
Browse files Browse the repository at this point in the history
Signed-off-by: SteveM <SteveM@myEtherWallet.com>

Co-authored-by: Sally MacFarlane <sally.macfarlane@consensys.net>
Co-authored-by: CJ Hare <CjHare@users.noreply.github.com>
Co-authored-by: Danno Ferrin <danno.ferrin@shemnon.com>
Co-authored-by: Ratan Rai Sur <ratan.r.sur@gmail.com>
Co-authored-by: Joshua Fernandes <joshua.fernandes@consensys.net>
  • Loading branch information
6 people committed Jan 10, 2020
1 parent db1294f commit ef61b54
Show file tree
Hide file tree
Showing 13 changed files with 818 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,8 @@ protected EthProtocolManager createEthProtocolManager(
syncConfig.getComputationParallelism(),
clock,
metricsSystem,
ethereumWireProtocolConfiguration);
ethereumWireProtocolConfiguration,
genesisConfig.getForks());
}

private List<PeerValidator> createPeerValidators(final ProtocolSchedule<C> protocolSchedule) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
package org.hyperledger.besu.config;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toUnmodifiableList;
import static org.hyperledger.besu.config.JsonUtil.normalizeKeys;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
Expand Down Expand Up @@ -131,6 +133,20 @@ public long getTimestamp() {
return parseLong("timestamp", JsonUtil.getValueAsString(configRoot, "timestamp", "0x0"));
}

public List<Long> getForks() {
return JsonUtil.getObjectNode(configRoot, "config").stream()
.flatMap(
node ->
Streams.stream(node.fieldNames())
.map(String::toLowerCase)
.filter(name -> !name.equals("chainid"))
.filter(name -> node.get(name).canConvertToLong())
.filter(name -> name.contains("block"))
.map(name -> node.get(name).asLong()))
.sorted()
.collect(toUnmodifiableList());
}

private String getRequiredString(final String key) {
if (!configRoot.has(key)) {
throw new IllegalArgumentException(
Expand Down
2 changes: 1 addition & 1 deletion config/src/main/resources/mainnet.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"config": {
"chainId": 1,
"homesteadBlock": 1150000,
"daoForkBlock": 1920000,
"homesteadBlock": 1150000,
"daoForkSupport": true,
"eip150Block": 2463000,
"eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,29 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.config.GenesisConfigFile.fromConfig;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.io.Resources;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.Test;

public class GenesisConfigFileTest {

private static final BigInteger MAINNET_CHAIN_ID = BigInteger.ONE;
private static final BigInteger DEVELOPMENT_CHAIN_ID = BigInteger.valueOf(2018);
private static final GenesisConfigFile EMPTY_CONFIG = GenesisConfigFile.fromConfig("{}");
private static final GenesisConfigFile EMPTY_CONFIG = fromConfig("{}");

@Test
public void shouldLoadMainnetConfigFile() {
Expand Down Expand Up @@ -143,7 +149,7 @@ public void shouldDefaultTimestampToZero() {
@Test
public void shouldGetAllocations() {
final GenesisConfigFile config =
GenesisConfigFile.fromConfig(
fromConfig(
"{"
+ " \"alloc\": {"
+ " \"fe3b557e8fb62b89f4916b721be55ceb828dbd73\": {"
Expand Down Expand Up @@ -193,14 +199,14 @@ public void shouldGetAllocations() {

@Test
public void shouldGetEmptyAllocationsWhenAllocNotPresent() {
final GenesisConfigFile config = GenesisConfigFile.fromConfig("{}");
final GenesisConfigFile config = fromConfig("{}");
assertThat(config.streamAllocations()).isEmpty();
}

@Test
public void shouldGetLargeChainId() {
final GenesisConfigFile config =
GenesisConfigFile.fromConfig(
fromConfig(
"{\"config\": { \"chainId\": 31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095 }}");
assertThat(config.getConfigOptions().getChainId())
.contains(
Expand All @@ -212,7 +218,7 @@ public void shouldGetLargeChainId() {
public void mustNotAcceptComments() {
assertThatThrownBy(
() ->
GenesisConfigFile.fromConfig(
fromConfig(
"{\"config\": { \"chainId\": 2017 }\n/* C comment }*/\n//C++ comment }\n}"))
.hasCauseInstanceOf(JsonParseException.class)
.hasMessageContaining("Unexpected character ('/'");
Expand All @@ -228,6 +234,7 @@ public void testOverridePresent() {
override.put("chainId", bigBlockString);
override.put("contractSizeLimit", bigBlockString);

assertThat(config.getForks()).isNotEmpty();
assertThat(config.getConfigOptions(override).getIstanbulBlockNumber()).hasValue(bigBlock);
assertThat(config.getConfigOptions(override).getChainId())
.hasValue(BigInteger.valueOf(bigBlock));
Expand All @@ -242,6 +249,7 @@ public void testOverrideNull() {
override.put("chainId", null);
override.put("contractSizeLimit", null);

assertThat(config.getForks()).isNotEmpty();
assertThat(config.getConfigOptions(override).getIstanbulBlockNumber()).isNotPresent();
assertThat(config.getConfigOptions(override).getChainId()).isNotPresent();
assertThat(config.getConfigOptions(override).getContractSizeLimit()).isNotPresent();
Expand Down Expand Up @@ -290,8 +298,29 @@ public void testNoOverride() {
assertThat(config.getConfigOptions().getEvmStackSize()).isNotPresent();
}

@Test
public void shouldLoadForksInSortedOrder() throws IOException {
final ObjectNode configNode =
new ObjectMapper()
.createObjectNode()
.set(
"config",
JsonUtil.objectNodeFromString(
Resources.toString(
Resources.getResource(
// If you inspect this config, you should see that fork block 2 is
// declared before 1
"valid_config_with_custom_forks.json"),
StandardCharsets.UTF_8)));

final GenesisConfigFile config = fromConfig(configNode);

assertThat(config.getForks()).containsExactly(1L, 2L, 3L, 3L, 1035301L);
assertThat(config.getConfigOptions().getChainId()).hasValue(BigInteger.valueOf(4));
}

private GenesisConfigFile configWithProperty(final String key, final String value) {
return GenesisConfigFile.fromConfig("{\"" + key + "\":\"" + value + "\"}");
return fromConfig("{\"" + key + "\":\"" + value + "\"}");
}

private void assertInvalidConfiguration(final ThrowingCallable getter) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"chainId": 4,
"homesteadBlock": 1,
"eip150Block": 2,
"homesteadBlock": 1,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 3,
"eip158Block": 3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class EthProtocol implements SubProtocol {
public static final String NAME = "eth";
public static final Capability ETH62 = Capability.create(NAME, EthVersion.V62);
public static final Capability ETH63 = Capability.create(NAME, EthVersion.V63);
public static final Capability ETH64 = Capability.create(NAME, EthVersion.V64);
private static final EthProtocol INSTANCE = new EthProtocol();

private static final List<Integer> eth62Messages =
Expand Down Expand Up @@ -120,5 +121,6 @@ public static EthProtocol get() {
public static class EthVersion {
public static final int V62 = 62;
public static final int V63 = 63;
public static final int V64 = 64;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
private final AtomicBoolean stopped = new AtomicBoolean(false);

private final Hash genesisHash;
private final ForkIdManager forkIdManager;
private final BigInteger networkId;
private final EthPeers ethPeers;
private final EthMessages ethMessages;
Expand All @@ -81,7 +82,8 @@ public EthProtocolManager(
final EthScheduler scheduler,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final Clock clock,
final MetricsSystem metricsSystem) {
final MetricsSystem metricsSystem,
final ForkIdManager forkIdManager) {
this.networkId = networkId;
this.peerValidators = peerValidators;
this.scheduler = scheduler;
Expand All @@ -91,6 +93,8 @@ public EthProtocolManager(
this.shutdown = new CountDownLatch(1);
genesisHash = blockchain.getBlockHashByNumber(0L).get();

this.forkIdManager = ForkIdManager.buildCollection(genesisHash);

ethPeers = new EthPeers(getSupportedProtocol(), clock, metricsSystem);
ethMessages = new EthMessages();
ethContext = new EthContext(ethPeers, ethMessages, scheduler);
Expand Down Expand Up @@ -126,7 +130,8 @@ public EthProtocolManager(
new EthScheduler(syncWorkers, txWorkers, computationWorkers, metricsSystem),
EthProtocolConfiguration.defaultConfig(),
clock,
metricsSystem);
metricsSystem,
ForkIdManager.buildCollection(blockchain.getBlockHashByNumber(0L).get()));
}

public EthProtocolManager(
Expand All @@ -150,7 +155,35 @@ public EthProtocolManager(
new EthScheduler(syncWorkers, txWorkers, computationWorkers, metricsSystem),
ethereumWireProtocolConfiguration,
clock,
metricsSystem);
metricsSystem,
ForkIdManager.buildCollection(blockchain.getBlockHashByNumber(0L).get()));
}

public EthProtocolManager(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final BigInteger networkId,
final List<PeerValidator> peerValidators,
final boolean fastSyncEnabled,
final int syncWorkers,
final int txWorkers,
final int computationWorkers,
final Clock clock,
final MetricsSystem metricsSystem,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final List<Long> forks) {
this(
blockchain,
worldStateArchive,
networkId,
peerValidators,
fastSyncEnabled,
new EthScheduler(syncWorkers, txWorkers, computationWorkers, metricsSystem),
ethereumWireProtocolConfiguration,
clock,
metricsSystem,
ForkIdManager.buildCollection(
blockchain.getBlockHashByNumber(0L).get(), forks, blockchain));
}

public EthContext ethContext() {
Expand Down Expand Up @@ -241,13 +274,16 @@ public void handleNewConnection(final PeerConnection connection) {
}

final Capability cap = connection.capability(getSupportedProtocol());
// TODO: look to consolidate code below if possible
// making status non-final and implementing it above would be one way.
final StatusMessage status =
StatusMessage.create(
cap.getVersion(),
networkId,
blockchain.getChainHead().getTotalDifficulty(),
blockchain.getChainHeadHash(),
genesisHash);
genesisHash,
forkIdManager.getLatestForkId());
try {
LOG.debug("Sending status message to {}.", peer);
peer.send(status);
Expand Down Expand Up @@ -284,7 +320,13 @@ private void handleStatusMessage(final EthPeer peer, final MessageData data) {
if (!status.networkId().equals(networkId)) {
LOG.debug("Disconnecting from peer with mismatched network id: {}", status.networkId());
peer.disconnect(DisconnectReason.SUBPROTOCOL_TRIGGERED);
} else if (!status.genesisHash().equals(genesisHash)) {
} else if (!forkIdManager.peerCheck(status.forkId()) && status.protocolVersion() > 63) {
LOG.debug(
"Disconnecting from peer with matching network id ({}), but non-matching fork id: {}",
networkId,
status.forkId());
peer.disconnect(DisconnectReason.SUBPROTOCOL_TRIGGERED);
} else if (forkIdManager.peerCheck(status.genesisHash())) {
LOG.debug(
"Disconnecting from peer with matching network id ({}), but non-matching genesis hash: {}",
networkId,
Expand Down
Loading

0 comments on commit ef61b54

Please sign in to comment.