Skip to content

Commit

Permalink
Add ephemery network config (hyperledger#7563)
Browse files Browse the repository at this point in the history
* Add Ephemery genesis config file

Signed-off-by: gconnect <[email protected]>

---------

Signed-off-by: gconnect <[email protected]>
Signed-off-by: Glory Agatevure <[email protected]>
Co-authored-by: Sally MacFarlane <[email protected]>Signed-off-by: Chulhee lee <[email protected]>
  • Loading branch information
gconnect and 2fehee committed Oct 4, 2024
1 parent 93dd929 commit 9e32b2b
Show file tree
Hide file tree
Showing 10 changed files with 1,180 additions and 10 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Changelog

## [Unreleased]
- Add `--ephemery` network support for Ephemery Testnet [#7563](https://github.com/hyperledger/besu/pull/7563) thanks to [@gconnect](https://github.com/gconnect)
- Add configuration of Consolidation Request Contract Address via genesis configuration [#7647](https://github.com/hyperledger/besu/pull/7647)

### Upcoming Breaking Changes
- k8s (KUBERNETES) Nat method is now deprecated and will be removed in a future release
Expand Down Expand Up @@ -4461,7 +4463,6 @@ Specify `*` or `all` for `--host-whitelist` to effectively disable host protecti
- Send client quitting disconnect message to peers on shutdown (PR [#253](https://github.com/PegaSysEng/pantheon/pull/253))
- Improved error message for port conflict error (PR [#232](https://github.com/PegaSysEng/pantheon/pull/232))


### Technical Improvements
- Upgraded Ethereum reference tests to 6.0 beta 2. (thanks to [@jvirtanen](https://github.com/jvirtanen) for the initial upgrade to beta 1)
- Set Java compiler default encoding to UTF-8 (PR [#238](https://github.com/PegaSysEng/pantheon/pull/238) thanks to [@matt9ucci](https://github.com/matt9ucci))
Expand Down
21 changes: 15 additions & 6 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,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 @@ -195,6 +196,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.EphemeryGenesisUpdater;
import org.hyperledger.besu.util.InvalidConfigurationException;
import org.hyperledger.besu.util.LogConfigurator;
import org.hyperledger.besu.util.NetworkUtility;
Expand Down Expand Up @@ -1602,11 +1604,14 @@ private void validateChainDataPruningParams() {
}

private GenesisConfigFile readGenesisConfigFile() {
final GenesisConfigFile effectiveGenesisFile =
genesisFile != null
? GenesisConfigFile.fromSource(genesisConfigSource(genesisFile))
: GenesisConfigFile.fromResource(
Optional.ofNullable(network).orElse(MAINNET).getGenesisFile());
GenesisConfigFile effectiveGenesisFile;
effectiveGenesisFile =
network.equals(EPHEMERY)
? EphemeryGenesisUpdater.updateGenesis(genesisConfigOverrides)
: genesisFile != null
? GenesisConfigFile.fromSource(genesisConfigSource(genesisFile))
: GenesisConfigFile.fromResource(
Optional.ofNullable(network).orElse(MAINNET).getGenesisFile());
return effectiveGenesisFile.withOverrides(genesisConfigOverrides);
}

Expand Down Expand Up @@ -2333,7 +2338,11 @@ private EthNetworkConfig updateNetworkConfig(final NetworkName network) {
if (networkId != null) {
builder.setNetworkId(networkId);
}

// ChainId update is required for Ephemery network
if (network.equals(EPHEMERY)) {
String chainId = genesisConfigOverrides.get("chainId");
builder.setNetworkId(new BigInteger(chainId));
}
if (p2PDiscoveryOptions.discoveryDnsUrl != null) {
builder.setDnsDiscoveryUrl(p2PDiscoveryOptions.discoveryDnsUrl);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public static EthNetworkConfig getNetworkConfig(final NetworkName networkName) {
strings ->
strings.stream().map(EnodeURLImpl::fromString).collect(Collectors.toList()))
.orElse(Collections.emptyList());

return new EthNetworkConfig(
genesisConfigFile,
networkName.getNetworkId(),
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 actual networkId used is calculated based on this default value and
* the current time. https://ephemery.dev/
*/
EPHEMERY("/ephemery.json", BigInteger.valueOf(39438135)),

/** Dev network name. */
DEV("/dev.json", BigInteger.valueOf(2018), false),
/** Future EIPs network name. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.Map;
import java.util.Optional;

/**
* The Generate Ephemery Genesis Updater. Checks for update based on the set period and update the
* Ephemery genesis in memory
*/
public class EphemeryGenesisUpdater {
private static final int PERIOD_IN_DAYS = 28;
private static final long PERIOD_IN_SECONDS = (PERIOD_IN_DAYS * 24 * 60 * 60);

/**
* Constructor for EphemeryGenesisUpdater. Initializes the genesis updater for the Ephemery
* network.
*/
public EphemeryGenesisUpdater() {}

/**
* Updates the Ephemery genesis configuration based on the predefined period.
*
* @param overrides a map of configuration overrides
* @return the updated GenesisConfigFile
* @throws RuntimeException if an error occurs during the update process
*/
public static GenesisConfigFile updateGenesis(final Map<String, String> overrides)
throws RuntimeException {
GenesisConfigFile genesisConfigFile;
try {
if (EPHEMERY.getGenesisFile() == null) {
throw new IOException("Genesis file or config options are null");
}
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_IN_DAYS;

long updatedTimestamp = genesisTimestamp + (periodsSinceGenesis * PERIOD_IN_SECONDS);
BigInteger updatedChainId =
genesisChainId
.orElseThrow(() -> new IllegalStateException("ChainId not present"))
.add(BigInteger.valueOf(periodsSinceGenesis));
// has a period elapsed since original ephemery genesis time? if so, override chainId and
// timestamp
if (currentTimestamp > (genesisTimestamp + PERIOD_IN_SECONDS)) {
overrides.put("chainId", String.valueOf(updatedChainId));
overrides.put("timestamp", String.valueOf(updatedTimestamp));
genesisConfigFile = genesisConfigFile.withOverrides(overrides);
}
return genesisConfigFile.withOverrides(overrides);
} catch (IOException e) {
throw new RuntimeException("Error updating ephemery genesis: " + e.getMessage(), e);
}
}
}
16 changes: 16 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 All @@ -43,6 +44,7 @@

import org.hyperledger.besu.BesuInfo;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.config.NetworkName;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.MergeConfiguration;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
Expand Down Expand Up @@ -1863,6 +1865,20 @@ 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(NetworkName.valueOf(String.valueOf(EPHEMERY))).isEqualTo(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,139 @@
/*
* 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.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.config.GenesisConfigFile.fromConfig;

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.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class EphemeryGenesisUpdaterTest {
private static final int GENESIS_CONFIG_TEST_CHAINID = 39438135;
private static final long GENESIS_TEST_TIMESTAMP = 1720119600;
private static final long EARLIER_TIMESTAMP = 1712041200;
private static final long LATER_TIMESTAMP = 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));

@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() {
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()))
.isEqualTo(String.valueOf(GENESIS_TEST_TIMESTAMP));
assertThat(config.getConfigOptions().getChainId())
.hasValue(BigInteger.valueOf(GENESIS_CONFIG_TEST_CHAINID));
assertThat(String.valueOf(config.getTimestamp())).isNotNull();
assertThat(String.valueOf(config.getTimestamp())).isNotEmpty();
}

@Test
public void testEphemeryNotYetDueForUpdate() {
final GenesisConfigFile config = GenesisConfigFile.fromConfig(VALID_GENESIS_JSON.toString());
assertThat(EARLIER_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).getConfigOptions().getChainId())
.hasValue(expectedChainId);
assertThat(config.withOverrides(override).getTimestamp()).isNotNull();
assertThat(expectedChainId).isEqualTo(override.get("chainId"));
assertThat(String.valueOf(expectedGenesisTimestamp)).isEqualTo(override.get("timestamp"));
}

@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);
final Map<String, String> override = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
override.put("chainId", String.valueOf(expectedChainId));
override.put("timestamp", String.valueOf(expectedGenesisTimestamp));
final GenesisConfigFile updatedConfig = config.withOverrides(override);

assertThat(LATER_TIMESTAMP)
.isGreaterThan(Long.parseLong(String.valueOf(GENESIS_TEST_TIMESTAMP + PERIOD_IN_SECONDS)));
assertThat(updatedConfig.getConfigOptions().getChainId()).hasValue(expectedChainId);
assertThat(updatedConfig.getTimestamp()).isEqualTo(expectedGenesisTimestamp);
assertThat(override.get("timestamp")).isEqualTo(String.valueOf(expectedGenesisTimestamp));
assertThat(override.get("chainId")).isEqualTo(expectedChainId.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ public Optional<String> getCoinbase() {
* @return the timestamp
*/
public long getTimestamp() {
if (overrides != null && overrides.containsKey("timestamp")) {
return Long.parseLong(overrides.get("timestamp"));
}
return parseLong("timestamp", JsonUtil.getValueAsString(genesisRoot, "timestamp", "0x0"));
}

Expand Down
Loading

0 comments on commit 9e32b2b

Please sign in to comment.