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

Commit

Permalink
972: Strategy for generating nodeId and nodePrivateKey
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucas Saldanha authored and lucassaldanha committed Jun 27, 2018
1 parent aa6e7d8 commit 03b4bd5
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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 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. By default the fallback strategy is the
* {@link GenerateNodeIdRandomly}.
*
* @author Lucas Saldanha
* @see GenerateNodeIdRandomly
* @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,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 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 file.
* <p>
* This strategy reads a nodeId and a nodePrivateKey from a File (nodeId.properties).
*
* @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

0 comments on commit 03b4bd5

Please sign in to comment.