From 68fc5afbdd34360906de7b6c270a7593ef7b8ed3 Mon Sep 17 00:00:00 2001 From: "C. Alexander Leigh" Date: Fri, 1 Apr 2022 11:54:44 -0700 Subject: [PATCH] leigh-chain-pki: WIP on PKI Tool --- leigh-chain-pki-pkitool/build.gradle | 6 +- .../java/leigh/chain/pki/pkitool/PKITool.java | 85 +++++++++++++++++++ .../main/java/leigh/chain/pki/NameClaim.java | 28 +++--- .../src/main/java/leigh/chain/pki/PKI.java | 34 ++++++++ .../java/leigh/chain/pki/PKIAddresses.java | 4 +- .../java/leigh/chain/pki/NameClaimTest.java | 2 +- .../src/main/java/leigh/chain/LeighChain.java | 67 +++++++++++++++ .../java/leigh/mecha/util/UniversalJob.java | 1 + 8 files changed, 208 insertions(+), 19 deletions(-) create mode 100644 leigh-chain-pki-pkitool/src/main/java/leigh/chain/pki/pkitool/PKITool.java create mode 100644 leigh-chain-pki/src/main/java/leigh/chain/pki/PKI.java create mode 100644 leigh-chain/src/main/java/leigh/chain/LeighChain.java diff --git a/leigh-chain-pki-pkitool/build.gradle b/leigh-chain-pki-pkitool/build.gradle index fa8ee3a8a..89f4dbf40 100644 --- a/leigh-chain-pki-pkitool/build.gradle +++ b/leigh-chain-pki-pkitool/build.gradle @@ -18,4 +18,8 @@ dependencies { test { useJUnitPlatform() -} \ No newline at end of file +} + +application { + mainClass = 'leigh.chain.pki.pkitool.PKITool' +} diff --git a/leigh-chain-pki-pkitool/src/main/java/leigh/chain/pki/pkitool/PKITool.java b/leigh-chain-pki-pkitool/src/main/java/leigh/chain/pki/pkitool/PKITool.java new file mode 100644 index 000000000..7901832ea --- /dev/null +++ b/leigh-chain-pki-pkitool/src/main/java/leigh/chain/pki/pkitool/PKITool.java @@ -0,0 +1,85 @@ +package leigh.chain.pki.pkitool; + +import leigh.chain.pki.PKI; +import leigh.mecha.json.JSONObject; +import leigh.mecha.log.MechaLogger; +import leigh.mecha.log.MechaLoggerFactory; +import leigh.mecha.util.UniversalJob; +import org.apache.commons.lang.StringUtils; +import org.web3j.crypto.CipherException; + +import java.io.IOException; + +/** + * The PKI tool is a low-level command-line tool for originating PKI transactions. Through + * the use of this tool it is possible to claim names, create secondaries, and issue + * burn notices. + *

+ * The following environment variables must be set to use this tool: + * + * LC_GETH_URL - The GETH RPC URL. e.g. http://localhost:8545 + * LC_PKI_PRIMARY_WALLET - The path to the primary wallet file. + * LC_PKI_PRIMARY_PW - The password for the primary wallet file. + *

+ * Upon normal operation the tool will output a JSON object to STDOUT containing relevant information. Additional + * information & logging may be provided to STDERR. + * + * @author C. Alexander Leigh + */ +public class PKITool { + private static final MechaLogger logger = MechaLoggerFactory.getLogger(PKITool.class); + public static String ENV_PRIMARY_WALLET = "LC_PKI_PRIMARY_WALLET"; + public static String ENV_PRIMARY_PW = "LC_PKI_PRIMARY_PW"; + public static String ENV_GETH_URL = "LC_GETH_URL"; + + public static void main(String[] args) throws CipherException, IOException { + UniversalJob.banner(logger, "PKITool"); + + String wallet = System.getenv(ENV_PRIMARY_WALLET); + String walletPw = System.getenv(ENV_PRIMARY_PW); + String gethUrl = System.getenv(ENV_GETH_URL); + + if (StringUtils.isEmpty(wallet)) { + logger.error(ENV_PRIMARY_WALLET + " environment variable must be set."); + System.exit(UniversalJob.RET_BADENV); + } + + if (StringUtils.isEmpty(walletPw)) { + logger.error(ENV_PRIMARY_PW + " environment variable must be set."); + System.exit(UniversalJob.RET_BADENV); + } + + if (StringUtils.isEmpty(gethUrl)) { + logger.error(ENV_GETH_URL + " environment variable must be set."); + System.exit(UniversalJob.RET_BADENV); + } + + if (args.length != 3) { + logger.info("Usage: PKITool name validityDays serviceUrl"); + System.exit(UniversalJob.RET_BADARGS); + } + + // PkiTool name validityPeriod serviceUrl + String name = args[0]; + int validityDays = Integer.parseInt(args[1]); + String serviceUrl = args[2]; + + String txnHash = PKI.submitNameClaim(gethUrl, wallet, walletPw, name, serviceUrl, validityDays); + + if (txnHash == null) { + logger.error("Submission failed."); + JSONObject jo = new JSONObject(); + jo.put("status", "error"); + System.out.println(jo); + System.exit(UniversalJob.RET_ERROR); + } + + logger.info("Name claim submitted. [txnHash: {}] [name: {}] [validity: {}] [serviceUrl: {}]", + txnHash, name, validityDays, serviceUrl); + + JSONObject jo = new JSONObject(); + jo.put("txnHash", txnHash); + jo.put("status", "ok"); + System.out.println(jo); + } +} diff --git a/leigh-chain-pki/src/main/java/leigh/chain/pki/NameClaim.java b/leigh-chain-pki/src/main/java/leigh/chain/pki/NameClaim.java index 79344662c..aa6bc9af4 100644 --- a/leigh-chain-pki/src/main/java/leigh/chain/pki/NameClaim.java +++ b/leigh-chain-pki/src/main/java/leigh/chain/pki/NameClaim.java @@ -9,20 +9,22 @@ import leigh.mecha.json.JSONObject; */ public class NameClaim { public static String KEY_NAME = "n"; - public static String KEY_SERVICEURL = "s"; - public static String KEY_VALIDDAYS = "v"; + public static String KEY_SERVICE_URL = "s"; + public static String KEY_VALIDITY_DAYS = "v"; public static int MAX_VALIDITY = 31 * 6; - private final String address; private final String name; - private final String serviceName; + private final String serviceUrl; private final int validityDays; - public NameClaim(String address, String name, String serviceName, int validityDays) { - this.address = address; + public NameClaim(String name, String serviceUrl, int validityDays) { this.name = name; - this.serviceName = serviceName; + this.serviceUrl = serviceUrl; this.validityDays = validityDays; + + if (validityDays < 0 || validityDays > MAX_VALIDITY) { + throw new IllegalArgumentException("Invalid validity period."); + } } /** @@ -34,21 +36,17 @@ public class NameClaim { public String toJSON() { JSONObject obj = new JSONObject(); obj.put(KEY_NAME, name); - obj.put(KEY_SERVICEURL, serviceName); - obj.put(KEY_VALIDDAYS, validityDays); + obj.put(KEY_SERVICE_URL, serviceUrl); + obj.put(KEY_VALIDITY_DAYS, validityDays); return obj.toString(); } - public String getAddress() { - return address; - } - public String getName() { return name; } - public String getServiceName() { - return serviceName; + public String getServiceUrl() { + return serviceUrl; } public int getValidityDays() { diff --git a/leigh-chain-pki/src/main/java/leigh/chain/pki/PKI.java b/leigh-chain-pki/src/main/java/leigh/chain/pki/PKI.java new file mode 100644 index 000000000..47e6dc04d --- /dev/null +++ b/leigh-chain-pki/src/main/java/leigh/chain/pki/PKI.java @@ -0,0 +1,34 @@ +package leigh.chain.pki; + +import leigh.chain.LeighChain; +import leigh.mecha.log.MechaLogger; +import leigh.mecha.log.MechaLoggerFactory; +import org.web3j.crypto.CipherException; +import org.web3j.crypto.WalletUtils; + +import java.io.IOException; + +/** + * This class provides convenience functions for interacting with the PKI on the LeighChain. + * + * @author Alex Leigh + */ +public class PKI { + private static final MechaLogger logger = MechaLoggerFactory.getLogger(PKI.class); + + /** + * Submit a name claim to the LeighChain. + * + * validityDays must be valid. If it is not, an exception will be thrown. + */ + public static String submitNameClaim(String gethUrl, String wallet, String walletPw, + String name, String serviceUrl, int validityDays) throws CipherException, IOException { + // This will throw if the validity period is illegal + NameClaim claim = new NameClaim(name, serviceUrl, validityDays); + + logger.info("Is name claim addr valid: {}", + WalletUtils.isValidAddress(PKIAddresses.OP_NAME_CLAIM)); + + return LeighChain.submit(gethUrl, wallet, walletPw, PKIAddresses.OP_NAME_CLAIM, claim.toJSON()); + } +} diff --git a/leigh-chain-pki/src/main/java/leigh/chain/pki/PKIAddresses.java b/leigh-chain-pki/src/main/java/leigh/chain/pki/PKIAddresses.java index e4e807c59..1944b3aab 100644 --- a/leigh-chain-pki/src/main/java/leigh/chain/pki/PKIAddresses.java +++ b/leigh-chain-pki/src/main/java/leigh/chain/pki/PKIAddresses.java @@ -1,10 +1,10 @@ package leigh.chain.pki; /** - * This class contains well known LEIGHChain addresses for various PKI transactions. + * This class contains well-known LEIGHChain addresses for various PKI transactions. * * @author Alex Leigh */ public class PKIAddresses { - public static String OP_NAME_CLAIM = "0x2948FbECa204b54d733bC7298416EA2d6f8fa3A1"; + public static String OP_NAME_CLAIM = "0x2948fbeca204b54d733bc7298416ea2d6f8fa3a1"; } diff --git a/leigh-chain-pki/src/test/java/leigh/chain/pki/NameClaimTest.java b/leigh-chain-pki/src/test/java/leigh/chain/pki/NameClaimTest.java index b9a08e663..50069522a 100644 --- a/leigh-chain-pki/src/test/java/leigh/chain/pki/NameClaimTest.java +++ b/leigh-chain-pki/src/test/java/leigh/chain/pki/NameClaimTest.java @@ -9,7 +9,7 @@ public class NameClaimTest { @Test public void testThing() { - NameClaim claim = new NameClaim("0x0", "leigh", + NameClaim claim = new NameClaim("leigh", "http://leigh-co.com/service.json", NameClaim.MAX_VALIDITY); String json = claim.toJSON(); logger.info("Built msg: {}", json); diff --git a/leigh-chain/src/main/java/leigh/chain/LeighChain.java b/leigh-chain/src/main/java/leigh/chain/LeighChain.java new file mode 100644 index 000000000..3fe431553 --- /dev/null +++ b/leigh-chain/src/main/java/leigh/chain/LeighChain.java @@ -0,0 +1,67 @@ +package leigh.chain; + +import leigh.mecha.log.MechaLogger; +import leigh.mecha.log.MechaLoggerFactory; +import org.web3j.crypto.*; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameterName; +import org.web3j.protocol.core.methods.response.EthGetTransactionCount; +import org.web3j.protocol.core.methods.response.EthSendTransaction; +import org.web3j.protocol.http.HttpService; +import org.web3j.utils.Convert; +import org.web3j.utils.Numeric; + +import java.io.IOException; +import java.math.BigInteger; + +/** + * This class implements convenience methods for interacting with the LeighChain. + * + * @author Alex Leigh + */ +public class LeighChain { + private static final MechaLogger logger = MechaLoggerFactory.getLogger(LeighChain.class); + + /** + * Submit a zero-value eth 1.0 transaction to the LeighChain via geth. The gas limit and + * price are set to 21,000. + *

+ * If successful, the transaction hash is returned. This hash can be used to monitor + * the process of the transaction to determine if it is eventually mined into a block. + */ + public static String submit(String gethUrl, String wallet, String walletPw, + String destAddr, String data) + throws CipherException, IOException { + Web3j web3 = Web3j.build(new HttpService(gethUrl)); + + Credentials credentials = WalletUtils.loadCredentials(walletPw, wallet); + + BigInteger gasLimit = BigInteger.valueOf(21000); + BigInteger gasPrice = Convert.toWei("21000", Convert.Unit.GWEI).toBigInteger(); + EthGetTransactionCount ethGetTransactionCount = web3.ethGetTransactionCount(credentials.getAddress(), + DefaultBlockParameterName.LATEST).send(); + BigInteger nonce = ethGetTransactionCount.getTransactionCount(); + + // There is basically a race here, since if something else publishes, we'll duplicate + // the nonce. There is basically nothing to be done about this. + + RawTransaction rawTransaction = RawTransaction.createTransaction( + nonce, + gasPrice, + gasLimit, + "destAddr", + BigInteger.valueOf(0), data); + + byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials); + + // Convert it to Hexadecimal String to be sent to the node + String hexValue = Numeric.toHexString(signedMessage); + EthSendTransaction ethSendTransaction = web3.ethSendRawTransaction(hexValue).send(); + + String txHash = ethSendTransaction.getTransactionHash(); + + logger.info("Transaction posted. [hash: {}]", txHash); + + return txHash; + } +} diff --git a/leigh-mecha/src/main/java/leigh/mecha/util/UniversalJob.java b/leigh-mecha/src/main/java/leigh/mecha/util/UniversalJob.java index 9c344326c..018debcb9 100644 --- a/leigh-mecha/src/main/java/leigh/mecha/util/UniversalJob.java +++ b/leigh-mecha/src/main/java/leigh/mecha/util/UniversalJob.java @@ -29,6 +29,7 @@ public final class UniversalJob { public static final int RET_ERROR = 3; // Bad properties file public static final int RET_PROPS = 4; + public static final int RET_BADENV = 5; private static String DEFAULT_COPYRIGHT = "2004-2022 Alex Leigh"; -- GitLab