Add ephemery network config (#7563)

* Add Ephemery genesis config file

Signed-off-by: gconnect <agatevureglory@gmail.com>

---------

Signed-off-by: gconnect <agatevureglory@gmail.com>
Signed-off-by: Glory Agatevure <agatevureglory@gmail.com>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/7722/head
Glory Agatevure 2 months ago committed by GitHub
parent 911f12efa0
commit 67d738c8a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      CHANGELOG.md
  2. 21
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  3. 1
      besu/src/main/java/org/hyperledger/besu/cli/config/EthNetworkConfig.java
  4. 6
      besu/src/main/java/org/hyperledger/besu/cli/config/NetworkName.java
  5. 81
      besu/src/main/java/org/hyperledger/besu/util/EphemeryGenesisUpdater.java
  6. 16
      besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
  7. 4
      besu/src/test/java/org/hyperledger/besu/cli/NetworkDeprecationMessageTest.java
  8. 139
      besu/src/test/java/org/hyperledger/besu/util/EphemeryGenesisUpdaterTest.java
  9. 3
      config/src/main/java/org/hyperledger/besu/config/GenesisConfigFile.java
  10. 916
      config/src/main/resources/ephemery.json

@ -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
@ -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))

@ -20,6 +20,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
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;
@ -195,6 +196,7 @@ import org.hyperledger.besu.services.TransactionPoolValidatorServiceImpl;
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;
@ -1602,11 +1604,14 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
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);
}
@ -2333,7 +2338,11 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
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 {

@ -77,6 +77,7 @@ public record EthNetworkConfig(
strings ->
strings.stream().map(EnodeURLImpl::fromString).collect(Collectors.toList()))
.orElse(Collections.emptyList());
return new EthNetworkConfig(
genesisConfigFile,
networkName.getNetworkId(),

@ -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. */

@ -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);
}
}
}

@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
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;
@ -43,6 +44,7 @@ import static org.mockito.Mockito.verify;
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;
@ -1863,6 +1865,20 @@ public class BesuCommandTest extends CommandTestAbstract {
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");

@ -39,9 +39,7 @@ class NetworkDeprecationMessageTest {
@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);

@ -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());
}
}

@ -280,6 +280,9 @@ public class GenesisConfigFile {
* @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"));
}

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save