Add Sepolia configs and address baseFee at genesis case (#2933)

Signed-off-by: garyschulte <garyschulte@gmail.com>
pull/2938/head
garyschulte 3 years ago committed by GitHub
parent ec5623f621
commit e3bf78cc08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 10
      besu/src/main/java/org/hyperledger/besu/cli/config/EthNetworkConfig.java
  3. 2
      besu/src/main/java/org/hyperledger/besu/cli/config/NetworkName.java
  4. 26
      config/src/main/java/org/hyperledger/besu/config/GenesisConfigFile.java
  5. 5
      config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java
  6. 30
      config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java
  7. 24
      config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java
  8. 41
      config/src/main/resources/sepolia.json
  9. 18
      config/src/test/java/org/hyperledger/besu/config/GenesisConfigFileTest.java
  10. 9
      config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java
  11. 1
      config/src/test/resources/all_forks.json
  12. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java
  13. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  14. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/FeeMarket.java
  15. 14
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarket.java
  16. 4
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/DiscoveryConfiguration.java

@ -8,9 +8,11 @@
- Optimize EVM Memory for MLOAD Operations [#2917](https://github.com/hyperledger/besu/pull/2917)
- Upgrade CircleCI OpenJDK docker image to version 11.0.12. [#2928](https://github.com/hyperledger/besu/pull/2928)
- Update JDK 11 to latest version in Besu Docker images. [#2925](https://github.com/hyperledger/besu/pull/2925)
- Add Sepolia proof-of-work testnet configurations [#2920](https://github.com/hyperledger/besu/pull/2920)
### Bug Fixes
- Do not change the sender balance, but set gas fee to zero, when simulating a transaction without enforcing balance checks. [#2454](https://github.com/hyperledger/besu/pull/2454)
- Ensure genesis block has the default base fee if london is at block 0 [#2920](https://github.com/hyperledger/besu/pull/2920)
### Early Access Features
- Enable plugins to expose custom JSON-RPC / WebSocket methods [#1317](https://github.com/hyperledger/besu/issues/1317)

@ -16,7 +16,6 @@ package org.hyperledger.besu.cli.config;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration.ASTOR_BOOTSTRAP_NODES;
import static org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration.CALAVERAS_BOOTSTRAP_NODES;
import static org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration.CLASSIC_BOOTSTRAP_NODES;
import static org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration.GOERLI_BOOTSTRAP_NODES;
import static org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration.GOERLI_DISCOVERY_URL;
@ -28,6 +27,7 @@ import static org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration.RI
import static org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration.RINKEBY_DISCOVERY_URL;
import static org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration.ROPSTEN_BOOTSTRAP_NODES;
import static org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration.ROPSTEN_DISCOVERY_URL;
import static org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration.SEPOLIA_BOOTSTRAP_NODES;
import org.hyperledger.besu.plugin.data.EnodeURL;
@ -47,7 +47,7 @@ public class EthNetworkConfig {
public static final BigInteger ROPSTEN_NETWORK_ID = BigInteger.valueOf(3);
public static final BigInteger RINKEBY_NETWORK_ID = BigInteger.valueOf(4);
public static final BigInteger GOERLI_NETWORK_ID = BigInteger.valueOf(5);
public static final BigInteger CALAVERAS_NETWORK_ID = BigInteger.valueOf(123);
public static final BigInteger SEPOLIA_NETWORK_ID = BigInteger.valueOf(11155111);
public static final BigInteger DEV_NETWORK_ID = BigInteger.valueOf(2018);
public static final BigInteger ECIP1049_DEV_NETWORK_ID = BigInteger.valueOf(2021);
public static final BigInteger CLASSIC_NETWORK_ID = BigInteger.valueOf(1);
@ -58,7 +58,7 @@ public class EthNetworkConfig {
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 CALAVERAS_GENESIS = "/calaveras.json";
private static final String SEPOLIA_GENESIS = "/sepolia.json";
private static final String DEV_GENESIS = "/dev.json";
private static final String DEV_ECIP1049_GENESIS = "/ecip1049_dev.json";
private static final String CLASSIC_GENESIS = "/classic.json";
@ -154,9 +154,9 @@ public class EthNetworkConfig {
GOERLI_NETWORK_ID,
GOERLI_BOOTSTRAP_NODES,
GOERLI_DISCOVERY_URL);
case CALAVERAS:
case SEPOLIA:
return new EthNetworkConfig(
jsonConfig(CALAVERAS_GENESIS), CALAVERAS_NETWORK_ID, CALAVERAS_BOOTSTRAP_NODES, null);
jsonConfig(SEPOLIA_GENESIS), SEPOLIA_NETWORK_ID, SEPOLIA_BOOTSTRAP_NODES, null);
case DEV:
return new EthNetworkConfig(
jsonConfig(DEV_GENESIS), DEV_NETWORK_ID, new ArrayList<>(), null);

@ -18,8 +18,8 @@ public enum NetworkName {
MAINNET,
RINKEBY,
ROPSTEN,
SEPOLIA,
GOERLI,
CALAVERAS,
DEV,
CLASSIC,
KOTTI,

@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.fasterxml.jackson.databind.node.ObjectNode;
@ -34,6 +35,7 @@ public class GenesisConfigFile {
public static final GenesisConfigFile DEFAULT =
new GenesisConfigFile(JsonUtil.createEmptyObjectNode());
public static final long BASEFEE_AT_GENESIS_DEFAULT_VALUE = 1000000000L;
private final ObjectNode configRoot;
private GenesisConfigFile(final ObjectNode config) {
@ -92,7 +94,20 @@ public class GenesisConfigFile {
public GenesisConfigOptions getConfigOptions(final Map<String, String> overrides) {
final ObjectNode config =
JsonUtil.getObjectNode(configRoot, "config").orElse(JsonUtil.createEmptyObjectNode());
return JsonGenesisConfigOptions.fromJsonObjectWithOverrides(config, overrides);
Map<String, String> overridesRef = overrides;
// if baseFeePerGas has been explicitly configured, pass it as an override:
final var optBaseFee = getBaseFeePerGas();
if (optBaseFee.isPresent()) {
overridesRef =
Streams.concat(
overrides.entrySet().stream(),
Stream.of(Map.entry("baseFeePerGas", optBaseFee.get().toString())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
return JsonGenesisConfigOptions.fromJsonObjectWithOverrides(config, overridesRef);
}
public Stream<GenesisAllocation> streamAllocations() {
@ -127,6 +142,15 @@ public class GenesisConfigFile {
.map(baseFeeStr -> parseLong("baseFeePerGas", baseFeeStr));
}
public Optional<Long> getGenesisBaseFeePerGas() {
// if we have a base fee market at genesis, get either the configured baseFeePerGas, or the
// default
return getBaseFeePerGas()
.map(Optional::of)
.orElseGet(() -> Optional.of(BASEFEE_AT_GENESIS_DEFAULT_VALUE))
.filter(z -> 0L == getConfigOptions().getLondonBlockNumber().orElse(-1L));
}
public String getMixHash() {
return JsonUtil.getString(configRoot, "mixhash", "");
}

@ -71,10 +71,7 @@ public interface GenesisConfigOptions {
OptionalLong getLondonBlockNumber();
// TODO EIP-1559 change for the actual fork name when known
OptionalLong getAleutBlockNumber();
OptionalLong getEIP1559BlockNumber();
OptionalLong getBaseFeePerGas();
List<Long> getForks();

@ -252,31 +252,15 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
@Override
public OptionalLong getLondonBlockNumber() {
final OptionalLong londonBlock = getOptionalLong("londonblock");
final OptionalLong calaverasblock = getOptionalLong("calaverasblock");
if (calaverasblock.isPresent()) {
if (londonBlock.isPresent()) {
throw new RuntimeException(
"Genesis files cannot specify both londonblock and calaverasblock.");
}
return calaverasblock;
}
return londonBlock;
}
@Override
public OptionalLong getAleutBlockNumber() {
return getOptionalLong("aleutblock");
return getOptionalLong("londonblock");
}
@Override
// TODO EIP-1559 change for the actual fork name when known
public OptionalLong getEIP1559BlockNumber() {
if (getAleutBlockNumber().isPresent()) {
return getAleutBlockNumber();
} else {
return getLondonBlockNumber();
}
public OptionalLong getBaseFeePerGas() {
return Optional.ofNullable(configOverrides.get("baseFeePerGas"))
.map(Long::parseLong)
.map(OptionalLong::of)
.orElse(OptionalLong.empty());
}
@Override
@ -405,7 +389,6 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
getMuirGlacierBlockNumber().ifPresent(l -> builder.put("muirGlacierBlock", l));
getBerlinBlockNumber().ifPresent(l -> builder.put("berlinBlock", l));
getLondonBlockNumber().ifPresent(l -> builder.put("londonBlock", l));
getAleutBlockNumber().ifPresent(l -> builder.put("aleutBlock", l));
// classic fork blocks
getClassicForkBlock().ifPresent(l -> builder.put("classicForkBlock", l));
@ -510,7 +493,6 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
getMuirGlacierBlockNumber(),
getBerlinBlockNumber(),
getLondonBlockNumber(),
getAleutBlockNumber(),
getEcip1015BlockNumber(),
getDieHardBlockNumber(),
getGothamBlockNumber(),

@ -37,9 +37,7 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions {
private OptionalLong muirGlacierBlockNumber = OptionalLong.empty();
private OptionalLong berlinBlockNumber = OptionalLong.empty();
private OptionalLong londonBlockNumber = OptionalLong.empty();
// TODO EIP-1559 change for the actual fork name when known
private final OptionalLong aleutBlockNumber = OptionalLong.empty();
private OptionalLong baseFeePerGas = OptionalLong.empty();
private OptionalLong classicForkBlock = OptionalLong.empty();
private OptionalLong ecip1015BlockNumber = OptionalLong.empty();
private OptionalLong diehardBlockNumber = OptionalLong.empty();
@ -181,18 +179,8 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions {
}
@Override
// TODO EIP-1559 change for the actual fork name when known
public OptionalLong getAleutBlockNumber() {
return aleutBlockNumber;
}
@Override
public OptionalLong getEIP1559BlockNumber() {
if (getAleutBlockNumber().isPresent()) {
return getAleutBlockNumber();
} else {
return getLondonBlockNumber();
}
public OptionalLong getBaseFeePerGas() {
return baseFeePerGas;
}
@Override
@ -295,7 +283,6 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions {
getMuirGlacierBlockNumber().ifPresent(l -> builder.put("muirGlacierBlock", l));
getBerlinBlockNumber().ifPresent(l -> builder.put("berlinBlock", l));
getLondonBlockNumber().ifPresent(l -> builder.put("londonBlock", l));
getAleutBlockNumber().ifPresent(l -> builder.put("aleutBlock", l));
// classic fork blocks
getClassicForkBlock().ifPresent(l -> builder.put("classicForkBlock", l));
getEcip1015BlockNumber().ifPresent(l -> builder.put("ecip1015Block", l));
@ -416,6 +403,11 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions {
return this;
}
public StubGenesisConfigOptions baseFeePerGas(final long baseFeeOverride) {
baseFeePerGas = OptionalLong.of(baseFeeOverride);
return this;
}
public StubGenesisConfigOptions classicForkBlock(final long blockNumber) {
classicForkBlock = OptionalLong.of(blockNumber);
return this;

@ -0,0 +1,41 @@
{
"config":{
"chainId":11155111,
"homesteadBlock":0,
"eip150Block":0,
"eip155Block":0,
"eip158Block":0,
"byzantiumBlock":0,
"constantinopleBlock":0,
"petersburgBlock":0,
"istanbulBlock":0,
"berlinBlock":0,
"londonBlock":0,
"ethash":{}
},
"alloc":{
"0xa2A6d93439144FFE4D27c9E088dCD8b783946263": {"balance": "0xD3C21BCECCEDA1000000"},
"0xBc11295936Aa79d594139de1B2e12629414F3BDB": {"balance": "0xD3C21BCECCEDA1000000"},
"0x7cF5b79bfe291A67AB02b393E456cCc4c266F753": {"balance": "0xD3C21BCECCEDA1000000"},
"0xaaec86394441f915bce3e6ab399977e9906f3b69": {"balance": "0xD3C21BCECCEDA1000000"},
"0xF47CaE1CF79ca6758Bfc787dbD21E6bdBe7112B8": {"balance": "0xD3C21BCECCEDA1000000"},
"0xd7eDDB78ED295B3C9629240E8924fb8D8874ddD8": {"balance": "0xD3C21BCECCEDA1000000"},
"0x8b7F0977Bb4f0fBE7076FA22bC24acA043583F5e": {"balance": "0xD3C21BCECCEDA1000000"},
"0xe2e2659028143784d557bcec6ff3a0721048880a": {"balance": "0xD3C21BCECCEDA1000000"},
"0xd9a5179f091d85051d3c982785efd1455cec8699": {"balance": "0xD3C21BCECCEDA1000000"},
"0xbeef32ca5b9a198d27B4e02F4c70439fE60356Cf": {"balance": "0xD3C21BCECCEDA1000000"},
"0x0000006916a87b82333f4245046623b23794c65c": {"balance": "0x84595161401484A000000"},
"0xb21c33de1fab3fa15499c62b59fe0cc3250020d1": {"balance": "0x52B7D2DCC80CD2E4000000"},
"0x10F5d45854e038071485AC9e402308cF80D2d2fE": {"balance": "0x52B7D2DCC80CD2E4000000"},
"0xd7d76c58b3a519e9fA6Cc4D22dC017259BC49F1E": {"balance": "0x52B7D2DCC80CD2E4000000"},
"0x799D329e5f583419167cD722962485926E338F4a": {"balance": "0xDE0B6B3A7640000"}
},
"coinbase":"0x0000000000000000000000000000000000000000",
"difficulty":"0x20000",
"extraData":"0x5365706f6c69612c20417468656e732c204174746963612c2047726565636521",
"gasLimit":"0x1c9c380",
"nonce":"0x000000000000000",
"mixhash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp":"0x6159af19"
}

@ -154,12 +154,28 @@ public class GenesisConfigFileTest {
public void shouldGetDefaultBaseFeeAtGenesis() {
GenesisConfigFile withBaseFeeAtGenesis =
GenesisConfigFile.fromConfig("{\"config\":{\"londonBlock\":0}}");
// no specified baseFeePerGas:
assertThat(withBaseFeeAtGenesis.getBaseFeePerGas()).isNotPresent();
// supply a default genesis baseFeePerGas when london-at-genesis:
assertThat(withBaseFeeAtGenesis.getGenesisBaseFeePerGas().get())
.isEqualTo(GenesisConfigFile.BASEFEE_AT_GENESIS_DEFAULT_VALUE);
}
@Test
public void shouldNotGetBaseFeeAtGenesis() {
assertThat(EMPTY_CONFIG.getBaseFeePerGas()).isNotPresent();
GenesisConfigFile withBaseFeeNotAtGenesis =
GenesisConfigFile.fromConfig("{\"config\":{\"londonBlock\":10},\"baseFeePerGas\":\"0xa\"}");
// specified baseFeePerGas:
assertThat(withBaseFeeNotAtGenesis.getBaseFeePerGas().get()).isEqualTo(10L);
// but no baseFeePerGas since london block is not at genesis:
assertThat(withBaseFeeNotAtGenesis.getGenesisBaseFeePerGas()).isNotPresent();
}
@Test
public void shouldOverrideConfigOptionsBaseFeeWhenSpecified() {
GenesisConfigOptions withOverrides =
EMPTY_CONFIG.getConfigOptions(Map.of("baseFeePerGas", "8"));
assertThat(withOverrides.getBaseFeePerGas().getAsLong()).isEqualTo(8L);
}
@Test

@ -181,14 +181,6 @@ public class GenesisConfigOptionsTest {
@Test
public void shouldGetLondonBlockNumber() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("londonblock", 1000));
assertThat(config.getEIP1559BlockNumber()).hasValue(1000);
assertThat(config.getLondonBlockNumber()).hasValue(1000);
}
@Test
public void shouldGetBaikalBlockNumber() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("calaverasblock", 1000));
assertThat(config.getEIP1559BlockNumber()).hasValue(1000);
assertThat(config.getLondonBlockNumber()).hasValue(1000);
}
@ -213,7 +205,6 @@ public class GenesisConfigOptionsTest {
assertThat(config.getMuirGlacierBlockNumber()).isEmpty();
assertThat(config.getBerlinBlockNumber()).isEmpty();
assertThat(config.getLondonBlockNumber()).isEmpty();
assertThat(config.getAleutBlockNumber()).isEmpty();
assertThat(config.getEcip1049BlockNumber()).isEmpty();
}

@ -11,7 +11,6 @@
"muirGlacierBlock": 9,
"berlinBlock": 10,
"londonBlock": 11,
"aleutBlock": 99,
"ecip1015Block": 102,
"dieHardBlock": 103,
"gothamBlock": 104,

@ -154,7 +154,7 @@ public final class GenesisState {
.mixHash(parseMixHash(genesis))
.nonce(parseNonce(genesis))
.blockHeaderFunctions(ScheduleBasedBlockHeaderFunctions.create(protocolSchedule))
.baseFee(genesis.getBaseFeePerGas().orElse(null))
.baseFee(genesis.getGenesisBaseFeePerGas().orElse(null))
.buildBlockHeader();
}

@ -506,8 +506,9 @@ public abstract class MainnetProtocolSpecs {
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);
final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE);
final long londonForkBlockNumber =
genesisConfigOptions.getEIP1559BlockNumber().orElse(Long.MAX_VALUE);
final BaseFeeMarket londonFeeMarket = FeeMarket.london(londonForkBlockNumber);
genesisConfigOptions.getLondonBlockNumber().orElse(Long.MAX_VALUE);
final BaseFeeMarket londonFeeMarket =
FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas());
return berlinDefinition(
chainId,
configContractSizeLimit,

@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.feemarket.TransactionPriceCalculator;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.function.Supplier;
public interface FeeMarket {
@ -33,7 +34,12 @@ public interface FeeMarket {
Transaction transaction, Supplier<Optional<Long>> baseFeeSupplier);
static BaseFeeMarket london(final long londonForkBlockNumber) {
return new LondonFeeMarket(londonForkBlockNumber);
return london(londonForkBlockNumber, OptionalLong.empty());
}
static BaseFeeMarket london(
final long londonForkBlockNumber, final OptionalLong baseFeePerGasOverride) {
return new LondonFeeMarket(londonForkBlockNumber, baseFeePerGasOverride);
}
static FeeMarket legacy() {

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet.feemarket;
import static java.lang.Math.max;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.feemarket.BaseFee;
@ -23,23 +24,32 @@ import org.hyperledger.besu.ethereum.core.feemarket.TransactionPriceCalculator;
import java.math.BigInteger;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.function.Supplier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class LondonFeeMarket implements BaseFeeMarket {
static final long DEFAULT_BASEFEE_INITIAL_VALUE = 1000000000L;
static final long DEFAULT_BASEFEE_INITIAL_VALUE =
GenesisConfigFile.BASEFEE_AT_GENESIS_DEFAULT_VALUE;
static final long DEFAULT_BASEFEE_MAX_CHANGE_DENOMINATOR = 8L;
static final long DEFAULT_SLACK_COEFFICIENT = 2L;
private static final Logger LOG = LogManager.getLogger();
private final long baseFeeInitialValue;
private final long londonForkBlockNumber;
private final TransactionPriceCalculator txPriceCalculator;
public LondonFeeMarket(final long londonForkBlockNumber) {
this(londonForkBlockNumber, OptionalLong.empty());
}
public LondonFeeMarket(
final long londonForkBlockNumber, final OptionalLong baseFeePerGasOverride) {
this.txPriceCalculator = TransactionPriceCalculator.eip1559();
this.londonForkBlockNumber = londonForkBlockNumber;
this.baseFeeInitialValue = baseFeePerGasOverride.orElse(DEFAULT_BASEFEE_INITIAL_VALUE);
}
@Override
@ -49,7 +59,7 @@ public class LondonFeeMarket implements BaseFeeMarket {
@Override
public long getInitialBasefee() {
return DEFAULT_BASEFEE_INITIAL_VALUE;
return baseFeeInitialValue;
}
@Override

@ -99,10 +99,10 @@ public class DiscoveryConfiguration {
.map(EnodeURLImpl::fromString)
.collect(toList()));
public static final List<EnodeURL> CALAVERAS_BOOTSTRAP_NODES =
public static final List<EnodeURL> SEPOLIA_BOOTSTRAP_NODES =
Collections.unmodifiableList(
Stream.of(
"enode://9e1096aa59862a6f164994cb5cb16f5124d6c992cdbf4535ff7dea43ea1512afe5448dca9df1b7ab0726129603f1a3336b631e4d7a1a44c94daddd03241587f9@3.9.20.133:30303")
"enode://7c9740e4d64674801fe62b76798d46778a038c49caebb15843d8c0f2b2f80d7ceba2585b4be366e6161988f81ddcfcd6fca98b5da52ae9a6f22c1b2a84b24a04@18.130.169.73:30303")
.map(EnodeURLImpl::fromString)
.collect(toList()));

Loading…
Cancel
Save