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

Add ephemery network config #7563

Merged
merged 40 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
3ff30b0
Add Ephemery genesis config file
gconnect Sep 3, 2024
34ac83b
Add Ephemery config file and test
gconnect Sep 3, 2024
5b6c600
Add EphemeryGenesisFile and test
gconnect Sep 3, 2024
79647ac
Add update to changelog.md
gconnect Sep 3, 2024
2dc65ef
Add spotlessApply and called the EphemeryGenesisFile in BesuCommand
gconnect Sep 3, 2024
929a6f9
Add bootnode and ethHash to genesis file and remove unnecessary comme…
gconnect Sep 3, 2024
31fa1ae
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 6, 2024
0191132
Update changelog,BesuCommand,EphemeryGenesisFile logic and test
gconnect Sep 6, 2024
4571df1
Remove print message and unused comment
gconnect Sep 6, 2024
07b9567
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 9, 2024
de62e90
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 9, 2024
ef828e6
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 11, 2024
db6f249
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 11, 2024
00b7c3b
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 13, 2024
c75a6d4
Add additional assertions to test, add comment to EphemeryGenesisFile…
gconnect Sep 13, 2024
29ae272
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 13, 2024
fb2ae86
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 13, 2024
ba6581c
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 17, 2024
57dc6c9
Rename files
gconnect Sep 18, 2024
e6005bc
Update test and ran spotlessApply
gconnect Sep 18, 2024
fc9a5e8
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 18, 2024
ef782dd
Update comment and test
gconnect Sep 18, 2024
7e1b31f
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 18, 2024
792a41a
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 20, 2024
00744d2
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 20, 2024
045fffa
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 24, 2024
87fdefd
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 24, 2024
b705a7a
Merge branch 'main' into add-ephemery-network-config
gconnect Sep 24, 2024
a80657d
Merge branch 'main' into add-ephemery-network-config
gconnect Oct 1, 2024
9556e5e
Merge branch 'main' into add-ephemery-network-config
gconnect Oct 1, 2024
12a101b
Change variable name in test and remove redudant assertions
gconnect Oct 1, 2024
7563eff
Merge branch 'main' into add-ephemery-network-config
macfarla Oct 2, 2024
63680ab
Merge branch 'main' into add-ephemery-network-config
gconnect Oct 2, 2024
9d8ddd3
Add comment to updateGenesis method
gconnect Oct 2, 2024
6567b77
Add a constructor with comment
gconnect Oct 2, 2024
80adada
Merge branch 'main' into add-ephemery-network-config
gconnect Oct 2, 2024
f263091
Merge branch 'main' into add-ephemery-network-config
gconnect Oct 3, 2024
e913743
Fix conflict in Besu command
gconnect Oct 3, 2024
465b54b
fix failing test
gconnect Oct 4, 2024
9805d5e
Merge branch 'main' into add-ephemery-network-config
macfarla Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4588,6 +4588,7 @@ Specify `*` or `all` for `--host-whitelist` to effectively disable host protecti
* [Logging](https://github.com/PegaSysEng/pantheon/wiki/Logging)
* [Proof of Authority](https://github.com/PegaSysEng/pantheon/wiki/Proof-of-Authority)
* [Passing JVM Options](https://github.com/PegaSysEng/pantheon/wiki/Passing-JVM-Options)
gconnect marked this conversation as resolved.
Show resolved Hide resolved
- Added `--ephemery` network support for Ephemery Testnet [#7471](https://github.com/hyperledger/besu/pull/7471) thanks to [@gconnect](https://github.com/gconnect)
gconnect marked this conversation as resolved.
Show resolved Hide resolved


### Technical Improvements
Expand Down
7 changes: 7 additions & 0 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.hyperledger.besu.cli.DefaultCommandValues.getDefaultBesuDataPath;
import static org.hyperledger.besu.cli.config.NetworkName.EPHEMERY;
import static org.hyperledger.besu.cli.config.NetworkName.MAINNET;
import static org.hyperledger.besu.cli.util.CommandLineUtils.DEPENDENCY_WARNING_MSG;
import static org.hyperledger.besu.cli.util.CommandLineUtils.isOptionSet;
Expand Down Expand Up @@ -196,6 +197,7 @@
import org.hyperledger.besu.services.TransactionSelectionServiceImpl;
import org.hyperledger.besu.services.TransactionSimulationServiceImpl;
import org.hyperledger.besu.services.kvstore.InMemoryStoragePlugin;
import org.hyperledger.besu.util.EphemeryGenesisFile;
import org.hyperledger.besu.util.InvalidConfigurationException;
import org.hyperledger.besu.util.LogConfigurator;
import org.hyperledger.besu.util.NetworkUtility;
Expand Down Expand Up @@ -339,6 +341,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {

private int maxPeers;
private int maxRemoteInitiatedPeers;
private EphemeryGenesisFile ephemeryGenesisFile;

// CLI options defined by user at runtime.
// Options parsing is done with CLI library Picocli https://picocli.info/
Expand Down Expand Up @@ -1656,6 +1659,10 @@ private GenesisConfigFile readGenesisConfigFile() {
? GenesisConfigFile.fromSource(genesisConfigSource(genesisFile))
: GenesisConfigFile.fromResource(
Optional.ofNullable(network).orElse(MAINNET).getGenesisFile());
if (network.equals(EPHEMERY)) {
ephemeryGenesisFile = new EphemeryGenesisFile();
ephemeryGenesisFile.updateGenesis();
}
gconnect marked this conversation as resolved.
Show resolved Hide resolved
return effectiveGenesisFile.withOverrides(genesisConfigOverrides);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ public enum NetworkName {
/** LUKSO mainnet network name. */
LUKSO("/lukso.json", BigInteger.valueOf(42)),

/**
* EPHEMERY network name. The networkId is the default networkId that will be used to make update
gconnect marked this conversation as resolved.
Show resolved Hide resolved
* to the most recent networkId.
gconnect marked this conversation as resolved.
Show resolved Hide resolved
*/
EPHEMERY("/ephemery.json", BigInteger.valueOf(39438135)),

/** Dev network name. */
DEV("/dev.json", BigInteger.valueOf(2018), false),
/** Future EIPs network name. */
Expand All @@ -43,7 +49,7 @@ public enum NetworkName {
MORDOR("/mordor.json", BigInteger.valueOf(7));

private final String genesisFile;
private final BigInteger networkId;
private BigInteger networkId;
private final boolean canSnapSync;
private final String deprecationDate;

Expand All @@ -68,6 +74,17 @@ public String getGenesisFile() {
return genesisFile;
}

/**
* This method is called only by the Ephemery network. It is required to update the networkid.
*
* @param networkId Sets network id .
*/
public void setNetworkId(final BigInteger networkId) {
gconnect marked this conversation as resolved.
Show resolved Hide resolved
if (this == EPHEMERY) {
this.networkId = networkId;
}
}

/**
* Gets network id.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.util;

import static org.hyperledger.besu.cli.config.NetworkName.EPHEMERY;

import org.hyperledger.besu.config.GenesisConfigFile;

import java.io.IOException;
import java.math.BigInteger;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
* The Generate Ephemery Genesis File. Checks for update based on the set period and update the
gconnect marked this conversation as resolved.
Show resolved Hide resolved
* Ephemery genesis file in memory
gconnect marked this conversation as resolved.
Show resolved Hide resolved
*/
public class EphemeryGenesisFile {
gconnect marked this conversation as resolved.
Show resolved Hide resolved
private static final int PERIOD = 28;
gconnect marked this conversation as resolved.
Show resolved Hide resolved
private static final long PERIOD_IN_SECONDS = (PERIOD * 24 * 60 * 60);

public void updateGenesis() {
try {
if (EPHEMERY.getGenesisFile() == null) {
throw new IOException("Genesis file or config options are null");
}
GenesisConfigFile genesisConfigFile =
GenesisConfigFile.fromResource(EPHEMERY.getGenesisFile());
long genesisTimestamp = genesisConfigFile.getTimestamp();
Optional<BigInteger> genesisChainId = genesisConfigFile.getConfigOptions().getChainId();
long currentTimestamp = Instant.now().getEpochSecond();
long periodsSinceGenesis =
ChronoUnit.DAYS.between(Instant.ofEpochSecond(genesisTimestamp), Instant.now()) / PERIOD;

long updatedTimestamp = genesisTimestamp + (periodsSinceGenesis * PERIOD_IN_SECONDS);
BigInteger updatedChainId =
genesisChainId
.orElseThrow(() -> new IllegalStateException("ChainId not present"))
.add(BigInteger.valueOf(periodsSinceGenesis));

if (currentTimestamp > (genesisTimestamp + PERIOD_IN_SECONDS)) {
EPHEMERY.setNetworkId(updatedChainId);
gconnect marked this conversation as resolved.
Show resolved Hide resolved
Map<String, String> overrides = new HashMap<>();
overrides.put("chainId", String.valueOf(updatedChainId));
overrides.put("timestamp", String.valueOf(updatedTimestamp));
gconnect marked this conversation as resolved.
Show resolved Hide resolved
genesisConfigFile.withOverrides(overrides);
}
} catch (IOException e) {
throw new RuntimeException("Error updating genesis file: " + e.getMessage(), e);
gconnect marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
17 changes: 17 additions & 0 deletions besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static org.hamcrest.Matchers.is;
import static org.hyperledger.besu.cli.config.NetworkName.CLASSIC;
import static org.hyperledger.besu.cli.config.NetworkName.DEV;
import static org.hyperledger.besu.cli.config.NetworkName.EPHEMERY;
import static org.hyperledger.besu.cli.config.NetworkName.EXPERIMENTAL_EIPS;
import static org.hyperledger.besu.cli.config.NetworkName.FUTURE_EIPS;
import static org.hyperledger.besu.cli.config.NetworkName.HOLESKY;
Expand Down Expand Up @@ -1862,6 +1863,22 @@ public void luksoValuesAreUsed() {
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}

@Test
public void ephemeryValuesAreUsed() {
parseCommand("--network", "ephemery");

final ArgumentCaptor<EthNetworkConfig> networkArg =
ArgumentCaptor.forClass(EthNetworkConfig.class);

verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any());
verify(mockControllerBuilder).build();

assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.getNetworkConfig(EPHEMERY));

assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}

@Test
public void classicValuesAreUsed() {
parseCommand("--network", "classic");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ void shouldGenerateDeprecationMessageForDeprecatedNetworks(final NetworkName net
@ParameterizedTest
@EnumSource(
value = NetworkName.class,
names = {
"MAINNET", "SEPOLIA", "DEV", "CLASSIC", "MORDOR", "HOLESKY", "LUKSO",
})
names = {"MAINNET", "SEPOLIA", "DEV", "CLASSIC", "MORDOR", "HOLESKY", "LUKSO", "EPHEMERY"})
void shouldThrowErrorForNonDeprecatedNetworks(final NetworkName network) {
assertThatThrownBy(() -> NetworkDeprecationMessage.generate(network))
.isInstanceOf(AssertionError.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.util;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.hyperledger.besu.cli.config.NetworkName.EPHEMERY;
import static org.hyperledger.besu.config.GenesisConfigFile.fromConfig;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import org.hyperledger.besu.config.GenesisConfigFile;

import java.math.BigInteger;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;

import io.vertx.core.json.JsonObject;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class EphemeryGenesisFileTest {
gconnect marked this conversation as resolved.
Show resolved Hide resolved
private EphemeryGenesisFile ephemeryGenesisFile;
private static final int GENESIS_CONFIG_TEST_CHAINID = 39438135;
private static final long GENESIS_TEST_TIMESTAMP = 1720119600;
private static final long CURRENT_TIMESTAMP = 1712041200;
private static final long CURRENT_TIMESTAMP_HIGHER = 1922041200;
private static final long PERIOD_IN_SECONDS = 28 * 24 * 60 * 60;
private static final long PERIOD_SINCE_GENESIS = 3;

private static final JsonObject VALID_GENESIS_JSON =
(new JsonObject())
.put("config", (new JsonObject()).put("chainId", GENESIS_CONFIG_TEST_CHAINID))
.put("timestamp", GENESIS_TEST_TIMESTAMP);

private static final GenesisConfigFile INVALID_GENESIS_JSON = fromConfig("{}");
private static final JsonObject INVALID_GENESIS_JSON_WITHOUT_CHAINID =
(new JsonObject()).put("timestamp", GENESIS_TEST_TIMESTAMP);

private static final JsonObject INVALID_GENESIS_JSON_WITHOUT_TIMESTAMP =
new JsonObject()
.put("config", (new JsonObject()).put("chainId", GENESIS_CONFIG_TEST_CHAINID));

@BeforeEach
void setUp() {
ephemeryGenesisFile = new EphemeryGenesisFile();
}

@Test
public void testEphemeryWhenChainIdIsAbsent() {
final GenesisConfigFile config =
GenesisConfigFile.fromConfig(INVALID_GENESIS_JSON_WITHOUT_CHAINID.toString());
Optional<BigInteger> chainId = config.getConfigOptions().getChainId();
assertThat(chainId).isNotPresent();
}

@Test
public void testShouldDefaultTimestampToZero() {
final GenesisConfigFile config =
GenesisConfigFile.fromConfig(INVALID_GENESIS_JSON_WITHOUT_TIMESTAMP.toString());
assertThat(config.getTimestamp()).isZero();
}

@Test
public void testEphemeryWhenGenesisJsonIsInvalid() {
Assertions.assertThatThrownBy(INVALID_GENESIS_JSON::getDifficulty)
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Invalid genesis block configuration");
}

@Test
public void testEphemeryWhenGenesisJsonIsValid() {
final GenesisConfigFile config = GenesisConfigFile.fromConfig(VALID_GENESIS_JSON.toString());
assertThat(String.valueOf(config.getTimestamp())).isNotNull();
assertThat(String.valueOf(config.getTimestamp())).isNotEmpty();
assertThat(String.valueOf(config.getConfigOptions().getChainId())).isNotNull();
assertThat(String.valueOf(config.getConfigOptions().getChainId())).isNotEmpty();
}

@Test
public void testEphemeryNotYetDueForUpdate() {
final GenesisConfigFile config = GenesisConfigFile.fromConfig(VALID_GENESIS_JSON.toString());
assertThat(CURRENT_TIMESTAMP).isLessThan(config.getTimestamp() + PERIOD_IN_SECONDS);
}

@Test
void testOverrideWithUpdatedChainIdAndTimeStamp() {
BigInteger expectedChainId =
BigInteger.valueOf(GENESIS_CONFIG_TEST_CHAINID)
.add(BigInteger.valueOf(PERIOD_SINCE_GENESIS));

long expectedGenesisTimestamp =
GENESIS_TEST_TIMESTAMP + (PERIOD_SINCE_GENESIS * PERIOD_IN_SECONDS);

final GenesisConfigFile config = GenesisConfigFile.fromResource("/ephemery.json");

final Map<String, String> override = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
override.put("chainId", String.valueOf(expectedChainId));
override.put("timestamp", String.valueOf(expectedGenesisTimestamp));

assertThat(config.withOverrides(override).getConfigOptions().getChainId()).isPresent();
assertThat(config.withOverrides(override).getTimestamp()).isNotNull();
gconnect marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
public void testEphemeryWhenSuccessful() {
final GenesisConfigFile config = GenesisConfigFile.fromConfig(VALID_GENESIS_JSON.toString());

BigInteger expectedChainId =
BigInteger.valueOf(GENESIS_CONFIG_TEST_CHAINID)
.add(BigInteger.valueOf(PERIOD_SINCE_GENESIS));

long expectedGenesisTimestamp =
GENESIS_TEST_TIMESTAMP + (PERIOD_SINCE_GENESIS * PERIOD_IN_SECONDS);

EPHEMERY.setNetworkId(expectedChainId);
gconnect marked this conversation as resolved.
Show resolved Hide resolved

final Map<String, String> override = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
override.put("chainId", String.valueOf(expectedChainId));
override.put("timestamp", String.valueOf(expectedGenesisTimestamp));
config.withOverrides(override);
gconnect marked this conversation as resolved.
Show resolved Hide resolved

assertThat(CURRENT_TIMESTAMP_HIGHER)
.isGreaterThan(Long.parseLong(String.valueOf(GENESIS_TEST_TIMESTAMP + PERIOD_IN_SECONDS)));

assertThat(override.get("timestamp")).isEqualTo(String.valueOf(expectedGenesisTimestamp));
assertThat(override.get("chainId")).isEqualTo(expectedChainId.toString());
gconnect marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
public void testEphemeryThrowIOException() {
EphemeryGenesisFile spyEphemeryGenesisFile = spy(ephemeryGenesisFile);

doThrow(new RuntimeException("Unable to update ephemery genesis file."))
.when(spyEphemeryGenesisFile)
.updateGenesis();

assertThatThrownBy(spyEphemeryGenesisFile::updateGenesis)
.isInstanceOf(RuntimeException.class)
.hasMessage("Unable to update ephemery genesis file.");
verify(spyEphemeryGenesisFile).updateGenesis();
}
}
Loading