/******************************************************************************* | |
* Copyright (c) 2020 RBEI and others. | |
* | |
* This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v. 2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* Contributors: | |
* Adhith Gopal - Initial API and Implementation | |
* Pavithra Krishna Reddy | |
* Deepthi Murugaiyan | |
* Abirami Bhologa Indiran | |
* Santhosh Gokhale D | |
*******************************************************************************/ | |
package org.eclipse.blockchain.core; | |
import java.io.BufferedReader; | |
import java.io.BufferedWriter; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.io.FileReader; | |
import java.io.FileWriter; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.io.PrintWriter; | |
import java.util.Enumeration; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.StringJoiner; | |
import java.util.zip.ZipEntry; | |
import java.util.zip.ZipFile; | |
import org.apache.commons.io.FileUtils; | |
import org.eclipse.blockchain.core.log.EthereumLogService; | |
import org.eclipse.blockchain.core.model.IOABI; | |
import org.eclipse.blockchain.core.model.SolidityABI; | |
import org.eclipse.blockchain.model.ethproject.EthereumProject; | |
import org.eclipse.core.resources.IFile; | |
import org.eclipse.core.resources.IResource; | |
import org.eclipse.core.resources.ResourcesPlugin; | |
import org.eclipse.core.runtime.FileLocator; | |
import org.eclipse.core.runtime.Platform; | |
import org.eclipse.core.runtime.preferences.InstanceScope; | |
import org.eclipse.osgi.internal.loader.EquinoxClassLoader; | |
import org.eclipse.osgi.internal.loader.classpath.ClasspathEntry; | |
import org.eclipse.osgi.util.NLS; | |
import org.eclipse.swt.widgets.Display; | |
import org.osgi.framework.Bundle; | |
import com.fasterxml.jackson.databind.ObjectMapper; | |
/** | |
* Code inside this class is just a prototype so it could contain | |
* duplicate/in-efficient code and maybe unwanted code SOLDITY COMPILATION IS | |
* TESTED WITH SOLC-0.5.4 version as of NOW | |
* | |
*/ | |
public class CoreCommandExecutor { | |
private Process gethServer = null; | |
private final String web3jVersion = "web3j-4.5.12"; | |
/** | |
* As of now only one Geth server instance is considered so everything related | |
* to geth is maintained as member variables | |
*/ | |
private String gethIPC = ""; | |
private String gethDataDirectory = ""; | |
private Map<String, String> gethOptions = null; | |
private final String solcCommand = "solc-path solidity-file --bin --abi --optimize -o output-directory"; | |
private final String web3jCommand = "web3j-path solidity generate -a=abi-path -b=bin-path -o=output-directory -p=com.bosch"; | |
private final Map<String, SolidityABI[]> solidityABIMap = new HashMap<>(); | |
private static CoreCommandExecutor instance; | |
private String gethServerStartError = ""; | |
private CoreCommandExecutor() { | |
} | |
/** | |
* This instance is used to handle functionality related to geth and solidity | |
* | |
* @return - CorecommandExecutor instance | |
*/ | |
public static CoreCommandExecutor getInstance() { | |
if (instance == null) { | |
instance = new CoreCommandExecutor(); | |
} | |
return instance; | |
} | |
String getGethServerStartError() { | |
return this.gethServerStartError; | |
} | |
/** | |
* To start/stop mining process | |
* | |
* @param start - if true mining will start/ if false mining will stop | |
* @throws IOException - | |
* @throws InterruptedException - | |
*/ | |
public void mine(final boolean start) throws IOException, InterruptedException { | |
Process miner = Runtime.getRuntime().exec("cmd"); | |
StringJoiner inputStream = new StringJoiner(System.lineSeparator()); | |
StringJoiner errorStream = new StringJoiner(System.lineSeparator()); | |
cmdRead(miner, inputStream, true); | |
cmdReadError(miner, errorStream, true); | |
PrintWriter writer = new PrintWriter(miner.getOutputStream()); | |
if (start) { | |
writer.println("geth attach " + this.gethIPC.replace("\\\\", "\\")); | |
writer.println("miner.start(3)"); | |
writer.close(); | |
miner.waitFor(); | |
miner.destroy(); | |
} else { | |
writer.println("geth attach " + this.gethIPC.replace("\\\\", "\\")); | |
writer.println("miner.stop()"); | |
writer.close(); | |
miner.waitFor(); | |
miner.destroy(); | |
} | |
} | |
private void transferArtifactsFromTempToOutput(final String tempLocation, final String outputLocation, | |
final String solName) throws IOException { | |
// ABI file | |
File abiTempFile = new File(tempLocation + File.separator + solName + ".abi"); | |
File abiFile = new File(outputLocation + File.separator + solName + ".abi"); | |
abiFile.createNewFile(); | |
FileUtils.copyFile(abiTempFile, abiFile); | |
// BIN file | |
File binTempFile = new File(tempLocation + File.separator + solName + ".bin"); | |
File binFile = new File(outputLocation + File.separator + solName + ".bin"); | |
abiFile.createNewFile(); | |
FileUtils.copyFile(binTempFile, binFile); | |
} | |
/** | |
* Some osgi internal API are accessed in this method to retrieve the classpath | |
* content of this plugin. It won't be changed until problems pop-up | |
* | |
* @param projectName - | |
* @param solidityFiles - | |
* @param outputDir - | |
* @return - | |
* @throws IOException - | |
* @throws InterruptedException - | |
* @throws ClassNotFoundException - | |
*/ | |
public String solidityCompile(final String projectName, final List<IResource> solidityFiles, | |
final IResource outputDir) throws IOException, InterruptedException, ClassNotFoundException { | |
String solcCompilerPath = InstanceScope.INSTANCE.getNode(SecoBlocksPreferenceConstants.SECOBLOCKS_PREF_NODE) | |
.get(SecoBlocksPreferenceConstants.SOLIDITY_COMPILER_PREF_KEY, ""); | |
String jdkPath = getJavaCPath(); | |
if (solcCompilerPath.isEmpty()) { | |
return "Compiler is not set... Please set it in solidity preference page"; | |
} | |
if(jdkPath.equals("\"\"")) { | |
return "Java-11 jdk should be either set in global path environment variable or jdk home path should be set in Secoblocks -> Java JDK Preference"; | |
} | |
for (IResource solFile : solidityFiles) { | |
String tempLocation = outputDir.getLocation().toOSString() + File.separator + "temp"; | |
File tempOutputDir = new File(tempLocation); | |
if (tempOutputDir.exists() && tempOutputDir.isDirectory()) { | |
tempOutputDir.delete(); | |
} | |
tempOutputDir.mkdirs();// Check This | |
String compileCommand = this.solcCommand | |
.replace("solc-path", getCmdLinePath(solcCompilerPath + File.separator + "solc")) | |
.replace("solidity-file", getCmdLinePath(solFile.getLocation().toOSString())) | |
.replace("output-directory", getCmdLinePath(tempOutputDir.getAbsolutePath())); | |
Process solcExe = Runtime.getRuntime().exec("cmd"); | |
StringJoiner inputStream = new StringJoiner(System.lineSeparator()); | |
StringJoiner errorStream = new StringJoiner(System.lineSeparator()); | |
cmdRead(solcExe, inputStream, false); | |
cmdReadError(solcExe, errorStream, false); | |
PrintWriter writer = new PrintWriter(solcExe.getOutputStream()); | |
writer.println(compileCommand); | |
writer.close(); | |
solcExe.waitFor(); | |
solcExe.destroy(); | |
if (inputStream.toString().contains("Error") || errorStream.toString().contains("Error")) { | |
EthereumLogService.INSTANCE.errorLog(inputStream.toString()); | |
EthereumLogService.INSTANCE.errorLog(errorStream.toString()); | |
System.err.println(inputStream.toString()); | |
System.err.println(errorStream.toString()); | |
return "Some errors occurred during compilation please check the console!!!"; | |
} | |
/** | |
* (In B.sol -> 2 contracts A(Abstract) and B), the actual implementation of A | |
* is present separately. In this case Abstract A's abi and bin replace actual | |
* A's abi and bin during solidity compilation. That is why this temp concept is | |
* introduced, when B.sol is compiled it generates B's abi and bin as well as | |
* Abstract A's abi and bin. This Abstract A is not required to be deployed but | |
* is required for solididty compilation. | |
*/ | |
transferArtifactsFromTempToOutput(tempOutputDir.getAbsolutePath(), outputDir.getLocation().toOSString(), | |
solFile.getName().replace(".sol", "")); | |
// Reading abi json | |
storeABIinSolidityMap( | |
outputDir.getLocation().toOSString() + File.separator + solFile.getName().replace(".sol", ".abi"), | |
solFile.getLocation().toOSString()); | |
// Solidity ABI javaType update | |
CoreCommandExecutor.getInstance().updateSolABIMap(solFile.getLocation().toOSString(), updateSolJavaTypes( | |
CoreCommandExecutor.getInstance().getSolABIMap(solFile.getLocation().toOSString()))); | |
// Remove old contract from deploymentModel | |
ProjectCreator.getInstance().removeDeploymentModelForResource(solFile, | |
Web3jHandler.getInstance().getSelectedEnvironment()); | |
// Java Wrapper Creation | |
String web3jCmd = this.web3jCommand.replace("web3j-path", "web3j") | |
.replace("abi-path", | |
getCmdLinePath(outputDir.getLocation().toOSString() + File.separator | |
+ solFile.getName().replace(".sol", ".abi"))) | |
.replace("bin-path", | |
getCmdLinePath(outputDir.getLocation().toOSString() + File.separator | |
+ solFile.getName().replace(".sol", ".bin"))) | |
.replace("output-directory", getCmdLinePath(outputDir.getLocation().toOSString())); | |
Process web3jExe = Runtime.getRuntime().exec("cmd"); | |
cmdRead(web3jExe, inputStream, true); | |
cmdReadError(web3jExe, errorStream, true); | |
writer = new PrintWriter(web3jExe.getOutputStream()); | |
/** | |
* The current directory is changed because web3j cli input string is limited | |
*/ | |
writer.println("cd " + getCmdLinePath(unzipAndGetWeb3jPath())); | |
writer.println("set JAVA_HOME=" + jdkPath); | |
//get jdk path if empty then do not proceed and show error | |
writer.println(web3jCmd); | |
ClassLoader classLoader = CoreCommandExecutor.class.getClassLoader(); | |
ClasspathEntry[] hostClasspathEntries = ((EquinoxClassLoader) classLoader).getClasspathManager() | |
.getHostClasspathEntries(); | |
String cp = ""; | |
for (ClasspathEntry c : hostClasspathEntries) { | |
cp = cp + c.getBundleFile().getBaseFile().toString() + ";"; | |
} | |
writer.println(jdkPath + "\\bin\\javac " | |
+ getCmdLinePath(outputDir.getLocation().toOSString() + File.separator + "com" + File.separator | |
+ "bosch" + File.separator + solFile.getName().replace(".sol", ".java")) | |
+ " -cp " + getCmdLinePath(cp)); | |
writer.close(); | |
web3jExe.waitFor(); | |
web3jExe.destroy(); | |
ProjectCreator.getInstance().addCompiledSolidityFiles(solFile); | |
FileUtils.deleteDirectory(tempOutputDir); | |
EthereumLogService.INSTANCE.log(inputStream.toString()); | |
EthereumLogService.INSTANCE.errorLog(errorStream.toString()); | |
System.err.println(inputStream.toString()); | |
System.err.println(errorStream.toString()); | |
} | |
return ""; | |
} | |
private String getCmdLinePath(final String path) { | |
return "\"" + path + "\""; | |
} | |
private SolidityABI[] updateSolJavaTypes(final SolidityABI[] solABI) throws ClassNotFoundException { | |
SolidityABI[] updatedSolABI = new SolidityABI[solABI.length]; | |
for (int i = 0; i < solABI.length; i++) { | |
updatedSolABI[i] = solABI[i]; | |
if (updatedSolABI[i].getInputs() != null) { | |
for (IOABI ioa : updatedSolABI[i].getInputs()) { | |
SolidityToJavaDataTypeResolver.getInstance().assignJavaType(ioa.getType(), ioa); | |
} | |
} | |
if (updatedSolABI[i].getOutputs() != null) { | |
for (IOABI ioa : updatedSolABI[i].getOutputs()) { | |
SolidityToJavaDataTypeResolver.getInstance().assignJavaType(ioa.getType(), ioa); | |
} | |
} | |
} | |
return updatedSolABI; | |
} | |
private String unzipAndGetWeb3jPath() throws IOException { | |
Bundle bundle = Platform.getBundle("org.eclipse.blockchain.core"); | |
return unzipWeb3jZip(FileLocator.toFileURL(bundle.getEntry("res/" + this.web3jVersion + ".zip")).getPath()); | |
} | |
private String unzipWeb3jZip(final String zipPath) throws IOException { | |
ZipFile web3jZip = new ZipFile(zipPath); | |
String destinationDir = ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString(); | |
File web3jZipContainer = new File(destinationDir + File.separator + "web3jContainer"); | |
if (!web3jZipContainer.exists()) { | |
web3jZipContainer.mkdir(); | |
final byte[] buf = new byte[4096]; | |
final Enumeration<? extends ZipEntry> zipEnum = web3jZip.entries(); | |
while (zipEnum.hasMoreElements()) { | |
final ZipEntry item = zipEnum.nextElement(); | |
if (item.isDirectory()) { | |
final File newdir = new File(web3jZipContainer + File.separator + item.getName()); | |
newdir.mkdir(); | |
} else { | |
final String newfilePath = web3jZipContainer + File.separator + item.getName(); | |
final File newFile = new File(newfilePath); | |
if (!newFile.getParentFile().exists()) { | |
newFile.getParentFile().mkdirs(); | |
} | |
try (InputStream is = web3jZip.getInputStream(item); | |
FileOutputStream fos = new FileOutputStream(newfilePath);) { | |
int len; | |
while ((len = is.read(buf)) > 0) { | |
fos.write(buf, 0, len); | |
} | |
is.close(); | |
fos.close(); | |
} | |
} | |
} | |
} | |
web3jZip.close(); | |
return web3jZipContainer.getAbsolutePath() + File.separator + this.web3jVersion + File.separator + "bin"; | |
} | |
/** | |
* @return - | |
* @throws IOException - | |
* @throws InterruptedException - | |
*/ | |
public String getJavaCPath() throws IOException, InterruptedException { | |
String jdkPath = ""; | |
Process process = Runtime.getRuntime().exec("cmd"); | |
PrintWriter writer = new PrintWriter(process.getOutputStream()); | |
StringJoiner readJDKPath = new StringJoiner(System.lineSeparator()); | |
cmdRead(process, readJDKPath, true); | |
cmdReadError(process, readJDKPath, true); | |
writer.println("where javac"); | |
writer.close(); | |
process.waitFor(); | |
String[] lines = readJDKPath.toString().split(System.lineSeparator()); | |
for (String line : lines) { | |
if (line.contains("javac.exe")) { | |
jdkPath = line.replace("\\bin\\javac.exe", "").replace("\\", "\\\\"); | |
break; | |
} | |
} | |
if(!isValidJDKPath(jdkPath)) { | |
jdkPath = ""; | |
//Retrieve java jdk path from preference | |
jdkPath = getJavaJDKFromPreference(); | |
if(!isValidJDKPath(jdkPath)) { | |
jdkPath = ""; | |
} | |
}else { | |
//Valid java jdk | |
//jdkPath = jdkPath + "\\bin\\javac.exe"; | |
} | |
process.destroy(); | |
return getCmdLinePath(jdkPath); | |
} | |
private String getJavaJDKFromPreference() { | |
return InstanceScope.INSTANCE.getNode(SecoBlocksPreferenceConstants.SECOBLOCKS_PREF_NODE).get(SecoBlocksPreferenceConstants.JAVA_JDK_PREF_KEY, ""); | |
} | |
public boolean isValidJDKPath(String jdkPath) throws IOException, InterruptedException { | |
boolean result = false; | |
Process process = Runtime.getRuntime().exec("cmd"); | |
PrintWriter writer = new PrintWriter(process.getOutputStream()); | |
StringJoiner jdkPathSJ = new StringJoiner(System.lineSeparator()); | |
//cmdRead(process, jdkPathSJ, true); | |
cmdReadError(process, jdkPathSJ, true); | |
if(!jdkPath.isEmpty()) { | |
//Jdk 11 check | |
writer.println(jdkPath+"\\bin\\java -version"); | |
writer.close(); | |
process.waitFor(); | |
String[] lines = jdkPathSJ.toString().split(System.lineSeparator()); | |
for (String line : lines) { | |
if (line.contains("version")) { | |
if(line.contains("11")) { | |
result = true; | |
} | |
break; | |
} | |
} | |
} | |
process.destroy(); | |
return result; | |
} | |
private void storeABIinSolidityMap(final String abiPath, final String solidityFilePath) throws IOException { | |
String abiJson = readABI(abiPath); | |
ObjectMapper jsonMapper = new ObjectMapper(); | |
SolidityABI[] readValue = jsonMapper.readValue(abiJson, SolidityABI[].class); | |
for (SolidityABI sabi : readValue) { | |
if (sabi.isPayable()) { | |
IOABI[] inputs = sabi.getInputs(); | |
IOABI[] newInputs = new IOABI[inputs.length + 1]; | |
for (int o = 0; o < inputs.length; o++) { | |
newInputs[o] = inputs[o]; | |
} | |
IOABI payableInput = new IOABI(); | |
payableInput.setName("payableAmount"); | |
payableInput.setType("uint256"); | |
newInputs[inputs.length] = payableInput; | |
sabi.setInputs(newInputs); | |
} | |
} | |
this.solidityABIMap.put(solidityFilePath, readValue); | |
} | |
/** | |
* @param solPath - The absolute path of solidity file | |
* @param onlyConstructor - If true will return only constructor ui components | |
* @return - The UI content to be constructed in transactions view | |
*/ | |
public Map<String, String> getUIComponentForSolidityFile(final String solPath, final boolean onlyConstructor, | |
final String projectName) { | |
SolidityABI[] solABi = this.solidityABIMap.get(solPath); | |
Map<String, String> uiMap = new HashMap<>(); | |
if (!onlyConstructor) {// Check to see whether the contract is deployed | |
// in current environment | |
EthereumProject ethProject = ProjectCreator.getInstance().getEthProject(projectName); | |
IFile file = ethProject.getProject() | |
.getFile(solPath.replace(ethProject.getProject().getLocation().toOSString(), "")); | |
if (!ProjectCreator.getInstance().isSCDeployed(file, Web3jHandler.getInstance().getSelectedEnvironment())) { | |
return uiMap; | |
} | |
} | |
if (solABi != null) { | |
for (SolidityABI sABI : solABi) { | |
if (sABI.getType().equalsIgnoreCase("constructor")) { | |
if (onlyConstructor) { | |
String key = "deploy"; | |
IOABI[] inputs = sABI.getInputs(); | |
String value = constructUIValue(inputs); | |
uiMap.put(key, value); | |
} | |
} else if (sABI.getType().equalsIgnoreCase("function")) { | |
if (!onlyConstructor) { | |
String key = sABI.getName(); | |
IOABI[] inputs = sABI.getInputs(); | |
String value = constructUIValue(inputs); | |
uiMap.put(key + "-" + value, value); | |
} | |
} | |
} | |
} | |
return uiMap; | |
} | |
private String constructUIValue(final IOABI[] inputs) { | |
StringJoiner uiElemString = new StringJoiner(";"); | |
for (IOABI input : inputs) { | |
uiElemString.add(input.getJavaType().getSimpleName()); | |
} | |
return uiElemString.toString(); | |
} | |
/** | |
* @param abiPath - | |
* @return - | |
*/ | |
public SolidityABI[] getSolABIMap(final String abiPath) { | |
return this.solidityABIMap.get(abiPath); | |
} | |
/** | |
* @param abiPath - | |
* @param updatedSolABI - | |
*/ | |
public void updateSolABIMap(final String abiPath, final SolidityABI[] updatedSolABI) { | |
this.solidityABIMap.put(abiPath, updatedSolABI); | |
} | |
private String readABI(final String abiPath) throws IOException { | |
StringJoiner sj = new StringJoiner(""); | |
try (BufferedReader br = new BufferedReader(new FileReader(new File(abiPath)))) { | |
String line = ""; | |
while ((line = br.readLine()) != null) { | |
sj.add(line); | |
} | |
} | |
return sj.toString(); | |
} | |
/** | |
* @return - | |
* @throws IOException - | |
* @throws InterruptedException - | |
*/ | |
public String isGethInstalled() throws IOException, InterruptedException { | |
/** | |
* This is not fully constructed but this should be first step check when | |
* someone tries to invoke ethereum project creation | |
*/ | |
StringJoiner inputStream = new StringJoiner(System.lineSeparator()); | |
StringJoiner errorStream = new StringJoiner(System.lineSeparator()); | |
String gethCheck = ""; | |
Process gethProcess = Runtime.getRuntime().exec("cmd"); | |
cmdRead(gethProcess, inputStream, true); | |
cmdReadError(gethProcess, errorStream, true); | |
PrintWriter processWriter = new PrintWriter(gethProcess.getOutputStream()); | |
processWriter.println("geth version"); | |
processWriter.close(); | |
gethProcess.waitFor(); | |
gethCheck = errorStream.toString(); | |
gethProcess.destroy(); | |
return gethCheck; | |
} | |
/** | |
* @param dataDir - | |
* @param initFilePath - | |
* @param localGethOptions - | |
* @return - Return if any error occurs during server start | |
* @throws IOException - | |
* @throws InterruptedException | |
*/ | |
public String startGethServer(final String dataDir, final String initFilePath, | |
final Map<String, String> localGethOptions) throws IOException, InterruptedException { | |
if (isGethInstalled() | |
.equals(NLS.bind(CoreCommandMessages.FRAMEWORK_NOT_INSTALLED, "'geth'", System.lineSeparator()))) { | |
return "Either Geth is not installed (or) its reference is not set in PATH env variable"; | |
} | |
if (!Web3jHandler.getInstance().getSelectedEnvironment() | |
.equals(SecoBlocksPreferenceConstants.EnvironmentType.GETH_CLIENT.toString())) { | |
return "Geth environment is not chosen in SecoBlocks -> Environment preference!!!"; | |
} | |
this.gethServerStartError = "Geth"; | |
StringJoiner inputStream = new StringJoiner(System.lineSeparator()); | |
StringJoiner errorStream = new StringJoiner(System.lineSeparator()); | |
this.gethDataDirectory = dataDir; | |
CoreCommandExecutor.getInstance().gethOptions = localGethOptions; | |
this.gethServer = Runtime.getRuntime().exec("cmd.exe"); | |
cmdRead(this.gethServer, inputStream, true); | |
cmdReadError(this.gethServer, errorStream, true); | |
String initFile = initFilePath; | |
String genesisFilePath = dataDir + File.separator + "genesis.json"; | |
if (new File(genesisFilePath).exists()) { | |
initFile = genesisFilePath; | |
} else { | |
if (initFilePath.isEmpty()) { | |
initFile = createGethInitFile(dataDir); | |
} | |
} | |
startGethServerLocal(dataDir, initFile, localGethOptions); | |
return ""; | |
} | |
private void startGethServerLocal(final String dataDir, final String initFile, | |
final Map<String, String> localGethOptions) { | |
PrintWriter processWriter = new PrintWriter(this.gethServer.getOutputStream()); | |
if (!initFile.isEmpty() && !new File(dataDir + File.separator + "geth").exists()) { | |
processWriter | |
.println("geth --datadir " + getCmdLinePath(dataDir) + " init " + getCmdLinePath(initFile) + ""); | |
} | |
String args = constructGethArguments(localGethOptions); | |
processWriter.println("geth --datadir " + getCmdLinePath(dataDir) + " " + args); | |
processWriter.close(); | |
/** | |
* This below code is required because geth once started won't terminate and it | |
* should not terminate because its a server | |
*/ | |
Display.getDefault().syncExec(() -> { | |
new Thread(() -> { | |
try { | |
this.gethServer.waitFor(); | |
} catch (InterruptedException e) { | |
BlockchainCore.getInstance().logException("", e.getMessage(), e); | |
} | |
}, "Geth Server").start(); | |
}); | |
} | |
/** | |
* @return - True if geth is started/ false otherwise | |
*/ | |
public boolean isGethStarted() { | |
return (this.gethServer != null) && this.gethServer.isAlive() && !this.gethIPC.isEmpty(); | |
} | |
/** | |
* @return - The geth server data directory | |
*/ | |
public String getDataDir() { | |
return this.gethDataDirectory; | |
} | |
/** | |
* @return -The geth command line options | |
*/ | |
public Map<String, String> getGethOptions() { | |
return this.gethOptions; | |
} | |
/** | |
* @return - Returns the default network id | |
*/ | |
public String getDefaultNetworkIdForGeth() { | |
String defaultNetworkId = ""; | |
try (BufferedReader br = new BufferedReader( | |
new InputStreamReader(CoreCommandExecutor.class.getResourceAsStream("genesis.template")));) { | |
String genesisContent = ""; | |
while ((genesisContent = br.readLine()) != null) { | |
if (genesisContent.contains("chainId")) { | |
defaultNetworkId = genesisContent.substring(genesisContent.indexOf(':') + 1).replace(",", "") | |
.trim(); | |
} | |
} | |
} catch (IOException e) { | |
BlockchainCore.getInstance().logException("", e.getMessage(), e); | |
} | |
return defaultNetworkId; | |
} | |
private String constructGethArguments(final Map<String, String> localGethOptions) { | |
StringJoiner arguments = new StringJoiner(" "); | |
localGethOptions.forEach((k, v) -> arguments.add("--" + k + "" + (v.isEmpty() ? "" : " " + v))); | |
return arguments.toString(); | |
} | |
/** | |
* Terminate geth server | |
*/ | |
public void terminateGethServer() { | |
if (CoreCommandExecutor.getInstance().gethServer != null) { | |
this.gethIPC = ""; | |
this.gethServer.destroyForcibly(); | |
try { | |
Process terminator = Runtime.getRuntime().exec("cmd.exe"); | |
PrintWriter pw = new PrintWriter(terminator.getOutputStream()); | |
pw.println("taskkill /F /IM geth.exe"); | |
pw.close(); | |
terminator.waitFor(); | |
terminator.destroy(); | |
} catch (IOException | InterruptedException e) { | |
BlockchainCore.getInstance().logException("", e.getMessage(), e); | |
} | |
} | |
} | |
private String createGethInitFile(final String dataDir) throws IOException { | |
File gethInitFile = new File(dataDir + File.separator + "genesis.json"); | |
gethInitFile.createNewFile();// NOSONAR | |
try (BufferedReader br = new BufferedReader( | |
new InputStreamReader(CoreCommandExecutor.class.getResourceAsStream("genesis.template"))); | |
BufferedWriter bw = new BufferedWriter(new FileWriter(gethInitFile));) { | |
String genesisContent = ""; | |
while ((genesisContent = br.readLine()) != null) { | |
bw.append(genesisContent); | |
bw.append(System.lineSeparator()); | |
} | |
} | |
return gethInitFile.getAbsolutePath(); | |
} | |
/** | |
* Read output stream of process going to be executed | |
* | |
* @param process - The process that is going to start | |
* @param inputStream - A log recording object | |
*/ | |
public void cmdRead(final Process process, final StringJoiner inputStream, final boolean logToConsole) { | |
Thread processReadThread = new Thread(() -> { | |
String line = ""; | |
try (BufferedReader processReader = new BufferedReader(new InputStreamReader(process.getInputStream()));) { | |
while ((line = processReader.readLine()) != null) { | |
inputStream.add(line.trim()); | |
if (line.contains("Error") && this.gethServerStartError.equals("Geth")) { | |
this.gethServerStartError = line; | |
} | |
if (line.contains("IPC endpoint opened")) { | |
/* | |
* To determine IPC endpoint when geth server is started. | |
*/ | |
this.gethIPC = line.substring(line.indexOf("url=") + 4); | |
} | |
if (logToConsole) { | |
EthereumLogService.INSTANCE.log(line); | |
System.err.println(line); | |
} | |
} | |
} catch (IOException e) { | |
BlockchainCore.getInstance().logException("", e.getMessage(), e); | |
} | |
}, "Process Read Thread"); | |
processReadThread.start(); | |
} | |
/** | |
* @return - the geth socket handle | |
*/ | |
public String getIPCPath() { | |
return this.gethIPC; | |
} | |
/** | |
* Read error stream of process going to be executed | |
* | |
* @param process - The process that is going to start | |
* @param errorStream - A log recording object | |
*/ | |
public void cmdReadError(final Process process, final StringJoiner errorStream, final boolean logToConsole) { | |
Thread processErrorThread = new Thread(() -> { | |
String line = ""; | |
try (BufferedReader processReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));) { | |
while ((line = processReader.readLine()) != null) { | |
errorStream.add(line.trim()); | |
if (line.contains("Error") && this.gethServerStartError.equals("Geth")) { | |
this.gethServerStartError = line; | |
} | |
if (line.contains("IPC endpoint opened")) {// To determine | |
// IPC endpoint | |
// when geth | |
// server is | |
// started. | |
this.gethIPC = line.substring(line.indexOf("url=") + 4); | |
} | |
if (logToConsole) { | |
EthereumLogService.INSTANCE.errorLog(line); | |
System.err.println(line); | |
} | |
} | |
} catch (IOException e) { | |
BlockchainCore.getInstance().logException("", e.getMessage(), e); | |
} | |
}, "Process Error Thread"); | |
processErrorThread.start(); | |
} | |
} |