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

Commit 121dfba

Browse files
authored
Merge pull request #1104 from lucassaldanha/issue-972
#972: Strategy for generating nodeId and nodePrivateKey
2 parents f0116f9 + 1aeed65 commit 121dfba

File tree

7 files changed

+410
-34
lines changed

7 files changed

+410
-34
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) [2017] [ <ether.camp> ]
3+
* This file is part of the ethereumJ library.
4+
*
5+
* The ethereumJ library is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* The ethereumJ library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package org.ethereum.config;
19+
20+
import org.ethereum.crypto.ECKey;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import org.spongycastle.util.encoders.Hex;
24+
25+
import java.io.File;
26+
import java.io.FileWriter;
27+
import java.io.IOException;
28+
import java.io.Writer;
29+
import java.util.Properties;
30+
31+
/**
32+
* Strategy to randomly generate the nodeId and the nodePrivateKey.
33+
*
34+
* @author Lucas Saldanha
35+
* @since 14.12.2017
36+
*/
37+
public class GenerateNodeIdRandomly implements GenerateNodeIdStrategy {
38+
39+
private static Logger logger = LoggerFactory.getLogger("general");
40+
41+
private String databaseDir;
42+
43+
GenerateNodeIdRandomly(String databaseDir) {
44+
this.databaseDir = databaseDir;
45+
}
46+
47+
@Override
48+
public String getNodePrivateKey() {
49+
ECKey key = new ECKey();
50+
Properties props = new Properties();
51+
props.setProperty("nodeIdPrivateKey", Hex.toHexString(key.getPrivKeyBytes()));
52+
props.setProperty("nodeId", Hex.toHexString(key.getNodeId()));
53+
54+
File file = new File(databaseDir, "nodeId.properties");
55+
file.getParentFile().mkdirs();
56+
try (Writer writer = new FileWriter(file)) {
57+
props.store(writer, "Generated NodeID. To use your own nodeId please refer to 'peer.privateKey' config option.");
58+
} catch (IOException e) {
59+
throw new RuntimeException(e);
60+
}
61+
62+
logger.info("New nodeID generated: " + props.getProperty("nodeId"));
63+
logger.info("Generated nodeID and its private key stored in " + file);
64+
65+
return props.getProperty("nodeIdPrivateKey");
66+
}
67+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) [2017] [ <ether.camp> ]
3+
* This file is part of the ethereumJ library.
4+
*
5+
* The ethereumJ library is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* The ethereumJ library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package org.ethereum.config;
19+
20+
/**
21+
* Strategy interface to generate the nodeId and the nodePrivateKey.
22+
* <p>
23+
* Two strategies are available:
24+
* <ul>
25+
* <li>{@link GetNodeIdFromPropsFile}: searches for a nodeId.properties
26+
* and uses the values in the file to set the nodeId and the nodePrivateKey.</li>
27+
* <li>{@link GenerateNodeIdRandomly}: generates a nodeId.properties file
28+
* with a generated nodeId and nodePrivateKey.</li>
29+
* </ul>
30+
*
31+
* @author Lucas Saldanha
32+
* @see SystemProperties#getGeneratedNodePrivateKey()
33+
* @since 14.12.2017
34+
*/
35+
public interface GenerateNodeIdStrategy {
36+
37+
String getNodePrivateKey();
38+
39+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) [2017] [ <ether.camp> ]
3+
* This file is part of the ethereumJ library.
4+
*
5+
* The ethereumJ library is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* The ethereumJ library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package org.ethereum.config;
19+
20+
import java.io.File;
21+
import java.io.FileReader;
22+
import java.io.IOException;
23+
import java.io.Reader;
24+
import java.util.Properties;
25+
26+
/**
27+
* Strategy to generate the nodeId and the nodePrivateKey from a nodeId.properties file.
28+
* <p>
29+
* If the nodeId.properties file doesn't exist, it uses the
30+
* {@link GetNodeIdFromPropsFile#fallbackGenerateNodeIdStrategy} as a fallback strategy
31+
* to generate the nodeId and nodePrivateKey.
32+
*
33+
* @author Lucas Saldanha
34+
* @since 14.12.2017
35+
*/
36+
public class GetNodeIdFromPropsFile implements GenerateNodeIdStrategy {
37+
38+
private String databaseDir;
39+
private GenerateNodeIdStrategy fallbackGenerateNodeIdStrategy;
40+
41+
GetNodeIdFromPropsFile(String databaseDir) {
42+
this.databaseDir = databaseDir;
43+
}
44+
45+
@Override
46+
public String getNodePrivateKey() {
47+
Properties props = new Properties();
48+
File file = new File(databaseDir, "nodeId.properties");
49+
if (file.canRead()) {
50+
try (Reader r = new FileReader(file)) {
51+
props.load(r);
52+
return props.getProperty("nodeIdPrivateKey");
53+
} catch (IOException e) {
54+
throw new RuntimeException("Error reading 'nodeId.properties' file", e);
55+
}
56+
} else {
57+
if (fallbackGenerateNodeIdStrategy != null) {
58+
return fallbackGenerateNodeIdStrategy.getNodePrivateKey();
59+
} else {
60+
throw new RuntimeException("Can't read 'nodeId.properties' and no fallback method has been set");
61+
}
62+
}
63+
}
64+
65+
public GenerateNodeIdStrategy withFallback(GenerateNodeIdStrategy generateNodeIdStrategy) {
66+
this.fallbackGenerateNodeIdStrategy = generateNodeIdStrategy;
67+
return this;
68+
}
69+
}

ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java

Lines changed: 22 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ static boolean isUseOnlySpringConfig() {
159159

160160
private final ClassLoader classLoader;
161161

162+
private GenerateNodeIdStrategy generateNodeIdStrategy = null;
163+
162164
public SystemProperties() {
163165
this(ConfigFactory.empty());
164166
}
@@ -217,21 +219,24 @@ public SystemProperties(Config apiConfig, ClassLoader classLoader) {
217219
// There could be several files with the same name from other packages,
218220
// "version.properties" is a very common name
219221
List<InputStream> iStreams = loadResources("version.properties", this.getClass().getClassLoader());
220-
for (InputStream is : iStreams) {
221-
Properties props = new Properties();
222-
props.load(is);
223-
if (props.getProperty("versionNumber") == null || props.getProperty("databaseVersion") == null) {
224-
continue;
225-
}
226-
this.projectVersion = props.getProperty("versionNumber");
227-
this.projectVersion = this.projectVersion.replaceAll("'", "");
222+
for (InputStream is : iStreams) {
223+
Properties props = new Properties();
224+
props.load(is);
225+
if (props.getProperty("versionNumber") == null || props.getProperty("databaseVersion") == null) {
226+
continue;
227+
}
228+
this.projectVersion = props.getProperty("versionNumber");
229+
this.projectVersion = this.projectVersion.replaceAll("'", "");
228230

229-
if (this.projectVersion == null) this.projectVersion = "-.-.-";
231+
if (this.projectVersion == null) this.projectVersion = "-.-.-";
230232

231-
this.projectVersionModifier = "master".equals(BuildInfo.buildBranch) ? "RELEASE" : "SNAPSHOT";
233+
this.projectVersionModifier = "master".equals(BuildInfo.buildBranch) ? "RELEASE" : "SNAPSHOT";
232234

233-
this.databaseVersion = Integer.valueOf(props.getProperty("databaseVersion"));
234-
break;
235+
this.databaseVersion = Integer.valueOf(props.getProperty("databaseVersion"));
236+
237+
this.generateNodeIdStrategy = new GetNodeIdFromPropsFile(databaseDir())
238+
.withFallback(new GenerateNodeIdRandomly(databaseDir()));
239+
break;
235240
}
236241
} catch (Exception e) {
237242
logger.error("Can't read config.", e);
@@ -677,28 +682,7 @@ public String privateKey() {
677682

678683
private String getGeneratedNodePrivateKey() {
679684
if (generatedNodePrivateKey == null) {
680-
try {
681-
File file = new File(databaseDir(), "nodeId.properties");
682-
Properties props = new Properties();
683-
if (file.canRead()) {
684-
try (Reader r = new FileReader(file)) {
685-
props.load(r);
686-
}
687-
} else {
688-
ECKey key = new ECKey();
689-
props.setProperty("nodeIdPrivateKey", Hex.toHexString(key.getPrivKeyBytes()));
690-
props.setProperty("nodeId", Hex.toHexString(key.getNodeId()));
691-
file.getParentFile().mkdirs();
692-
try (Writer w = new FileWriter(file)) {
693-
props.store(w, "Generated NodeID. To use your own nodeId please refer to 'peer.privateKey' config option.");
694-
}
695-
logger.info("New nodeID generated: " + props.getProperty("nodeId"));
696-
logger.info("Generated nodeID and its private key stored in " + file);
697-
}
698-
generatedNodePrivateKey = props.getProperty("nodeIdPrivateKey");
699-
} catch (IOException e) {
700-
throw new RuntimeException(e);
701-
}
685+
generatedNodePrivateKey = generateNodeIdStrategy.getNodePrivateKey();
702686
}
703687
return generatedNodePrivateKey;
704688
}
@@ -972,4 +956,8 @@ public boolean githubTestsLoadLocal() {
972956
return config.hasPath("GitHubTests.testPath") &&
973957
!config.getString("GitHubTests.testPath").isEmpty();
974958
}
959+
960+
void setGenerateNodeIdStrategy(GenerateNodeIdStrategy generateNodeIdStrategy) {
961+
this.generateNodeIdStrategy = generateNodeIdStrategy;
962+
}
975963
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (c) [2017] [ <ether.camp> ]
3+
* This file is part of the ethereumJ library.
4+
*
5+
* The ethereumJ library is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* The ethereumJ library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package org.ethereum.config;
19+
20+
import org.junit.Test;
21+
import org.springframework.util.FileCopyUtils;
22+
import org.springframework.util.StringUtils;
23+
24+
import javax.annotation.concurrent.NotThreadSafe;
25+
import java.io.File;
26+
import java.io.FileReader;
27+
28+
import static org.junit.Assert.*;
29+
30+
/**
31+
* Not thread safe - testGeneratedNodePrivateKey temporarily removes the nodeId.properties
32+
* file which may influence other tests.
33+
*/
34+
@SuppressWarnings("ConstantConditions")
35+
@NotThreadSafe
36+
public class GenerateNodeIdRandomlyTest {
37+
38+
@Test
39+
public void testGenerateNodeIdRandomlyCreatesFileWithNodeIdAndPrivateKey() throws Exception {
40+
File nodeIdPropertiesFile = new File("database-test/nodeId.properties");
41+
//Cleanup previous nodeId.properties file (if exists)
42+
//noinspection ResultOfMethodCallIgnored
43+
nodeIdPropertiesFile.delete();
44+
45+
new GenerateNodeIdRandomly("database-test").getNodePrivateKey();
46+
47+
assertTrue(nodeIdPropertiesFile.exists());
48+
String contents = FileCopyUtils.copyToString(new FileReader(nodeIdPropertiesFile));
49+
String[] lines = StringUtils.tokenizeToStringArray(contents, "\n");
50+
assertEquals(4, lines.length);
51+
assertTrue(lines[0].startsWith("#Generated NodeID."));
52+
assertTrue(lines[1].startsWith("#"));
53+
assertTrue(lines[2].startsWith("nodeIdPrivateKey="));
54+
assertEquals("nodeIdPrivateKey=".length() + 64, lines[2].length());
55+
assertTrue(lines[3].startsWith("nodeId="));
56+
assertEquals("nodeId=".length() + 128, lines[3].length());
57+
58+
//noinspection ResultOfMethodCallIgnored
59+
nodeIdPropertiesFile.delete();
60+
}
61+
62+
}

0 commit comments

Comments
 (0)