Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

#972: Strategy for generating nodeId and nodePrivateKey #1104

Merged
merged 1 commit into from
Jul 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) [2017] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ethereumJ library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.config;

import org.ethereum.crypto.ECKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Properties;

/**
* Strategy to randomly generate the nodeId and the nodePrivateKey.
*
* @author Lucas Saldanha
* @since 14.12.2017
*/
public class GenerateNodeIdRandomly implements GenerateNodeIdStrategy {

private static Logger logger = LoggerFactory.getLogger("general");

private String databaseDir;

GenerateNodeIdRandomly(String databaseDir) {
this.databaseDir = databaseDir;
}

@Override
public String getNodePrivateKey() {
ECKey key = new ECKey();
Properties props = new Properties();
props.setProperty("nodeIdPrivateKey", Hex.toHexString(key.getPrivKeyBytes()));
props.setProperty("nodeId", Hex.toHexString(key.getNodeId()));

File file = new File(databaseDir, "nodeId.properties");
file.getParentFile().mkdirs();
try (Writer writer = new FileWriter(file)) {
props.store(writer, "Generated NodeID. To use your own nodeId please refer to 'peer.privateKey' config option.");
} catch (IOException e) {
throw new RuntimeException(e);
}

logger.info("New nodeID generated: " + props.getProperty("nodeId"));
logger.info("Generated nodeID and its private key stored in " + file);

return props.getProperty("nodeIdPrivateKey");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) [2017] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ethereumJ library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.config;

/**
* Strategy interface to generate the nodeId and the nodePrivateKey.
* <p>
* Two strategies are available:
* <ul>
* <li>{@link GetNodeIdFromPropsFile}: searches for a nodeId.properties
* and uses the values in the file to set the nodeId and the nodePrivateKey.</li>
* <li>{@link GenerateNodeIdRandomly}: generates a nodeId.properties file
* with a generated nodeId and nodePrivateKey.</li>
* </ul>
*
* @author Lucas Saldanha
* @see SystemProperties#getGeneratedNodePrivateKey()
* @since 14.12.2017
*/
public interface GenerateNodeIdStrategy {

String getNodePrivateKey();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) [2017] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ethereumJ library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.config;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Properties;

/**
* Strategy to generate the nodeId and the nodePrivateKey from a nodeId.properties file.
* <p>
* If the nodeId.properties file doesn't exist, it uses the
* {@link GetNodeIdFromPropsFile#fallbackGenerateNodeIdStrategy} as a fallback strategy
* to generate the nodeId and nodePrivateKey.
*
* @author Lucas Saldanha
* @since 14.12.2017
*/
public class GetNodeIdFromPropsFile implements GenerateNodeIdStrategy {

private String databaseDir;
private GenerateNodeIdStrategy fallbackGenerateNodeIdStrategy;

GetNodeIdFromPropsFile(String databaseDir) {
this.databaseDir = databaseDir;
}

@Override
public String getNodePrivateKey() {
Properties props = new Properties();
File file = new File(databaseDir, "nodeId.properties");
if (file.canRead()) {
try (Reader r = new FileReader(file)) {
props.load(r);
return props.getProperty("nodeIdPrivateKey");
} catch (IOException e) {
throw new RuntimeException("Error reading 'nodeId.properties' file", e);
}
} else {
if (fallbackGenerateNodeIdStrategy != null) {
return fallbackGenerateNodeIdStrategy.getNodePrivateKey();
} else {
throw new RuntimeException("Can't read 'nodeId.properties' and no fallback method has been set");
}
}
}

public GenerateNodeIdStrategy withFallback(GenerateNodeIdStrategy generateNodeIdStrategy) {
this.fallbackGenerateNodeIdStrategy = generateNodeIdStrategy;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ static boolean isUseOnlySpringConfig() {

private final ClassLoader classLoader;

private GenerateNodeIdStrategy generateNodeIdStrategy = null;

public SystemProperties() {
this(ConfigFactory.empty());
}
Expand Down Expand Up @@ -217,21 +219,24 @@ public SystemProperties(Config apiConfig, ClassLoader classLoader) {
// There could be several files with the same name from other packages,
// "version.properties" is a very common name
List<InputStream> iStreams = loadResources("version.properties", this.getClass().getClassLoader());
for (InputStream is : iStreams) {
Properties props = new Properties();
props.load(is);
if (props.getProperty("versionNumber") == null || props.getProperty("databaseVersion") == null) {
continue;
}
this.projectVersion = props.getProperty("versionNumber");
this.projectVersion = this.projectVersion.replaceAll("'", "");
for (InputStream is : iStreams) {
Properties props = new Properties();
props.load(is);
if (props.getProperty("versionNumber") == null || props.getProperty("databaseVersion") == null) {
continue;
}
this.projectVersion = props.getProperty("versionNumber");
this.projectVersion = this.projectVersion.replaceAll("'", "");

if (this.projectVersion == null) this.projectVersion = "-.-.-";
if (this.projectVersion == null) this.projectVersion = "-.-.-";

this.projectVersionModifier = "master".equals(BuildInfo.buildBranch) ? "RELEASE" : "SNAPSHOT";
this.projectVersionModifier = "master".equals(BuildInfo.buildBranch) ? "RELEASE" : "SNAPSHOT";

this.databaseVersion = Integer.valueOf(props.getProperty("databaseVersion"));
break;
this.databaseVersion = Integer.valueOf(props.getProperty("databaseVersion"));

this.generateNodeIdStrategy = new GetNodeIdFromPropsFile(databaseDir())
.withFallback(new GenerateNodeIdRandomly(databaseDir()));
break;
}
} catch (Exception e) {
logger.error("Can't read config.", e);
Expand Down Expand Up @@ -677,28 +682,7 @@ public String privateKey() {

private String getGeneratedNodePrivateKey() {
if (generatedNodePrivateKey == null) {
try {
File file = new File(databaseDir(), "nodeId.properties");
Properties props = new Properties();
if (file.canRead()) {
try (Reader r = new FileReader(file)) {
props.load(r);
}
} else {
ECKey key = new ECKey();
props.setProperty("nodeIdPrivateKey", Hex.toHexString(key.getPrivKeyBytes()));
props.setProperty("nodeId", Hex.toHexString(key.getNodeId()));
file.getParentFile().mkdirs();
try (Writer w = new FileWriter(file)) {
props.store(w, "Generated NodeID. To use your own nodeId please refer to 'peer.privateKey' config option.");
}
logger.info("New nodeID generated: " + props.getProperty("nodeId"));
logger.info("Generated nodeID and its private key stored in " + file);
}
generatedNodePrivateKey = props.getProperty("nodeIdPrivateKey");
} catch (IOException e) {
throw new RuntimeException(e);
}
generatedNodePrivateKey = generateNodeIdStrategy.getNodePrivateKey();
}
return generatedNodePrivateKey;
}
Expand Down Expand Up @@ -972,4 +956,8 @@ public boolean githubTestsLoadLocal() {
return config.hasPath("GitHubTests.testPath") &&
!config.getString("GitHubTests.testPath").isEmpty();
}

void setGenerateNodeIdStrategy(GenerateNodeIdStrategy generateNodeIdStrategy) {
this.generateNodeIdStrategy = generateNodeIdStrategy;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) [2017] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ethereumJ library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.config;

import org.junit.Test;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;

import javax.annotation.concurrent.NotThreadSafe;
import java.io.File;
import java.io.FileReader;

import static org.junit.Assert.*;

/**
* Not thread safe - testGeneratedNodePrivateKey temporarily removes the nodeId.properties
* file which may influence other tests.
*/
@SuppressWarnings("ConstantConditions")
@NotThreadSafe
public class GenerateNodeIdRandomlyTest {

@Test
public void testGenerateNodeIdRandomlyCreatesFileWithNodeIdAndPrivateKey() throws Exception {
File nodeIdPropertiesFile = new File("database-test/nodeId.properties");
//Cleanup previous nodeId.properties file (if exists)
//noinspection ResultOfMethodCallIgnored
nodeIdPropertiesFile.delete();

new GenerateNodeIdRandomly("database-test").getNodePrivateKey();

assertTrue(nodeIdPropertiesFile.exists());
String contents = FileCopyUtils.copyToString(new FileReader(nodeIdPropertiesFile));
String[] lines = StringUtils.tokenizeToStringArray(contents, "\n");
assertEquals(4, lines.length);
assertTrue(lines[0].startsWith("#Generated NodeID."));
assertTrue(lines[1].startsWith("#"));
assertTrue(lines[2].startsWith("nodeIdPrivateKey="));
assertEquals("nodeIdPrivateKey=".length() + 64, lines[2].length());
assertTrue(lines[3].startsWith("nodeId="));
assertEquals("nodeId=".length() + 128, lines[3].length());

//noinspection ResultOfMethodCallIgnored
nodeIdPropertiesFile.delete();
}

}
Loading