Java tutorial
/** * */ package de.urszeidler.ethereum.licencemanager1.deployer; import static org.ethereum.util.ByteUtil.bigIntegerToBytes; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.adridadou.ethereum.propeller.EthereumFacade; import org.adridadou.ethereum.propeller.keystore.AccountProvider; import org.adridadou.ethereum.propeller.keystore.FileSecureKey; import org.adridadou.ethereum.propeller.keystore.SecureKey; import org.adridadou.ethereum.propeller.values.EthAccount; import org.adridadou.ethereum.propeller.values.EthAddress; import org.adridadou.ethereum.propeller.values.EthData; import org.adridadou.ethereum.propeller.values.EthValue; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionGroup; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.io.IOUtils; import org.bouncycastle.util.encoders.Hex; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.ECKey.ECDSASignature; import com.google.common.primitives.Bytes; import de.urszeidler.ethereum.licencemanager1.EthereumInstance; import de.urszeidler.ethereum.licencemanager1.EthereumInstance.DeployDuo; import de.urszeidler.ethereum.licencemanager1.contracts.LicenseIssuer; import de.urszeidler.ethereum.licencemanager1.contracts.LicenseIssuerIssuedLicense; import de.urszeidler.ethereum.licencemanager1.contracts.LicenseManager; /** * @author * */ public class LicenseManagerDeployer { public static final long FINNEY_TO_WEI = 1000000000000000L; private EthereumFacade ethereum; private ContractsDeployer deployer; private long millis; private EthAccount sender; private DeployDuo<LicenseManager> licenseManager; private interface DoAndWaitOneTime<T> { boolean isDone(); CompletableFuture<T> doIt(); } public LicenseManagerDeployer() { super(); ethereum = EthereumInstance.getInstance().getEthereum(); deployer = new ContractsDeployer(ethereum, "/contracts/combined.json", true); } public static byte[] createMessageHash(String message) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-256"); return md.digest(message.getBytes()); } public static String toPublicKeyString(EthAccount account1) { byte[] encoded = account1.getPublicKey().getEncoded(false); return Hex.toHexString(encoded); } public static String createSignature(EthAccount account1, String message) throws NoSuchAlgorithmException { byte[] messageHash = createMessageHash(message); ECKey private1 = ECKey.fromPrivate(account1.getBigIntPrivateKey()); ECDSASignature signature = private1.sign(messageHash); return signature.toHex(); } /** * @param args */ public static void main(String[] args) { Options options = createOptions(); CommandLineParser parser = new DefaultParser(); int returnValue = 0; boolean dontExit = false; try { CommandLine commandLine = parser.parse(options, args); if (commandLine.hasOption("h")) { printHelp(options); return; } if (commandLine.hasOption("de")) dontExit = true; String senderKey = null; String senderPass = null; if (commandLine.hasOption("sk")) senderKey = commandLine.getOptionValue("sk"); if (commandLine.hasOption("sp")) senderPass = commandLine.getOptionValue("sp"); LicenseManagerDeployer manager = new LicenseManagerDeployer(); try { manager.init(senderKey, senderPass); long currentMili = 0; EthValue balance = null; if (commandLine.hasOption("calcDeploymendCost")) { currentMili = System.currentTimeMillis(); balance = manager.ethereum.getBalance(manager.sender); } if (commandLine.hasOption("observeBlock")) { manager.ethereum.events().observeBlocks() .subscribe(b -> System.out.println("Block: " + b.blockNumber + " " + b.receipts)); } if (commandLine.hasOption("f")) { String[] values = commandLine.getOptionValues("f"); String filename = values[0]; String isCompiled = values[1]; manager.deployer.setContractSource(filename, Boolean.parseBoolean(isCompiled)); } if (commandLine.hasOption("millis")) { manager.setMillis(Long.parseLong(commandLine.getOptionValue("millis"))); } if (commandLine.hasOption("c")) { String[] values = commandLine.getOptionValues("c"); if (values == null || values.length != 2) { System.out.println("Error. Need 2 parameters: paymentAddress,name"); System.out.println(""); printHelp(options); return; } String paymentAddress = values[0]; String name = values[1]; manager.deployLicenseManager(EthAddress.of(paymentAddress), name); } else if (commandLine.hasOption("l")) { String contractAddress = commandLine.getOptionValue("l"); if (contractAddress == null) { System.out.println("Error. Need 1 parameters: contract address"); System.out.println(""); printHelp(options); return; } manager.setManager(EthAddress.of(contractAddress)); manager.listContractData(EthAddress.of(contractAddress)); } else if (commandLine.hasOption("cic")) { String[] values = commandLine.getOptionValues("cic"); if (values == null || values.length != 6) { System.out.println("Error. Need 6 itemName, textHash, url, lifeTime, price"); System.out.println(""); printHelp(options); return; } String contractAddress = values[0]; String itemName = values[1]; String textHash = values[2]; String url = values[3]; String lifeTime = values[4]; String price = values[5]; manager.setManager(EthAddress.of(contractAddress)); manager.createIssuerContract(itemName, textHash, url, Integer.parseInt(lifeTime), Integer.parseInt(price)); } else if (commandLine.hasOption("bli")) { String[] values = commandLine.getOptionValues("bli"); if (values == null || values.length < 2 || values.length > 3) { System.out.println( "Error. Need 2-3 issuerAddress, name, optional an address when not use the sender."); System.out.println(""); printHelp(options); return; } String issuerAddress = values[0]; String name = values[1]; String address = values.length > 2 ? values[2] : null; manager.buyLicense(issuerAddress, name, address); } else if (commandLine.hasOption("v")) { String[] values = commandLine.getOptionValues("v"); String issuerAddress = values[0]; String message = values[1]; String signature = values[2]; String publicKey = values[3]; manager.verifyLicense(issuerAddress, message, signature, publicKey); } else if (commandLine.hasOption("cs")) { String message = commandLine.getOptionValue("cs"); if (message == null) { System.out.println("Error. Need 1 parameter: message"); System.out.println(""); printHelp(options); return; } String signature = createSignature(manager.sender, message); String publicKeyString = toPublicKeyString(manager.sender); System.out.println("The signature for the message is:"); System.out.println(signature); System.out.println("The public key is:"); System.out.println(publicKeyString); } else if (commandLine.hasOption("co")) { String[] values = commandLine.getOptionValues("co"); if (values == null || values.length != 2) { System.out.println("Error. Need 2 parameters: contractAddress, newOwnerAddress"); System.out.println(""); printHelp(options); return; } String contractAddress = values[0]; String newOwner = values[1]; manager.changeOwner(EthAddress.of(contractAddress), EthAddress.of(newOwner)); } else if (commandLine.hasOption("si")) { String contractAddress = commandLine.getOptionValue("si"); if (contractAddress == null) { System.out.println("Error. Need 1 parameters: contract address"); System.out.println(""); printHelp(options); return; } manager.setManager(EthAddress.of(contractAddress)); manager.stopIssue(contractAddress); } else if (commandLine.hasOption("ppuk")) { System.out.println("Public key: " + toPublicKeyString(manager.sender)); } if (manager.licenseManager != null && commandLine.hasOption("wca")) { String[] values = commandLine.getOptionValues("wca"); String filename = values[0]; File file = new File(filename); IOUtils.write( !commandLine.hasOption("cic") ? manager.licenseManager.contractAddress.withLeading0x() : manager.licenseManager.contractInstance .contracts(manager.licenseManager.contractInstance.contractCount() - 1) .withLeading0x(), new FileOutputStream(file), "UTF-8"); } if (commandLine.hasOption("calcDeploymendCost")) { balance = balance.minus(manager.ethereum.getBalance(manager.sender)); BigDecimal divide = new BigDecimal(balance.inWei()) .divide(BigDecimal.valueOf(1_000_000_000_000_000_000L)); System.out.println("Deployment cost: " + (divide) + " in wei:" + balance.inWei() + " time need: " + (System.currentTimeMillis() - currentMili)); } } catch (Exception e) { System.out.println(e.getMessage()); printHelp(options); returnValue = 10; } } catch (ParseException e1) { System.out.println(e1.getMessage()); printHelp(options); returnValue = 10; } if (!dontExit) System.exit(returnValue); } private void stopIssue(String contractAddress) throws IOException, InterruptedException, ExecutionException { LicenseIssuer issuerProxy = deployer.createLicenseIssuerProxy(sender, EthAddress.of(contractAddress)); issuerProxy.stopIssuing(); } private void changeOwner(EthAddress contract, EthAddress newOwner) throws IOException, InterruptedException, ExecutionException { LicenseManager licenseManagerProxy = deployer.createLicenseManagerProxy(sender, contract); licenseManagerProxy.changeOwner(newOwner); listContractData(contract); } public void verifyLicense(String issuerAddress, String message, String signature, String publicKey) throws IOException, InterruptedException, ExecutionException, NoSuchAlgorithmException { System.out.println("Verify license: [" + message + "] " + signature + " puk: " + publicKey); LicenseIssuer licenseIssuer = deployer.createLicenseIssuerProxy(sender, EthAddress.of(issuerAddress)); if (!licenseIssuer.getIssuable()) throw new RuntimeException("The license is no longer issuable."); byte[] messageHash = createMessageHash(message); byte[] decode_Signature = Hex.decode(signature); byte[] pub = Hex.decode(publicKey); byte v = decode_Signature[64]; byte[] r = new byte[32]; byte[] s = new byte[32]; System.arraycopy(decode_Signature, 0, r, 0, 32); System.arraycopy(decode_Signature, 32, s, 0, 32); ECDSASignature ecdSignature = ECDSASignature.fromComponents(r, s, v); if (!ECKey.verify(messageHash, ecdSignature, pub)) { throw new RuntimeException("Message did not match signature."); } // special case when v is 0 it was properly 27 see // org.ethereum.crypto.ECKey.ECDSASignature.toByteArray() if (v == 0) v = 27; if (!licenseIssuer.checkLicense(EthData.of(messageHash), (int) v, EthData.of(r), EthData.of(s))) throw new RuntimeException("The license is not valid."); } public void buyLicense(String issuerAddress, String name, String address) throws IOException, InterruptedException, ExecutionException { LicenseIssuer licenseIssuer = deployer.createLicenseIssuerProxy(sender, EthAddress.of(issuerAddress)); if (!licenseIssuer.getIssuable()) throw new RuntimeException("The license is no longer issuable."); EthAddress eaddress = (address == null || address.isEmpty()) ? EthAddress.empty() : EthAddress.of(address); BigInteger licencePrice = licenseIssuer.licencePrice(); Integer licenseCount = licenseIssuer.licenseCount(); doAndWait("Buying license " + licenseIssuer.licensedItemName() + " for " + licencePrice + " wei", new DoAndWaitOneTime<Void>() { @Override public boolean isDone() { return licenseIssuer.licenseCount() == licenseCount + 1; } @Override public CompletableFuture<Void> doIt() { return licenseIssuer.buyLicense(eaddress, name).with(EthValue.wei(licencePrice)); } }); } /** * Create a new issuer contract. * * @param itemName * @param textHash * @param url * @param lifeTime * @param price * @throws InterruptedException * @throws ExecutionException * @throws IOException */ public void createIssuerContract(String itemName, String textHash, String url, Integer lifeTime, Integer price) throws InterruptedException, ExecutionException, IOException { Integer contractCount = licenseManager.contractInstance.contractCount(); doAndWait("Create a new issuer contract: " + itemName + " the hash: " + textHash, new DoAndWaitOneTime<Void>() { @Override public boolean isDone() { return licenseManager.contractInstance.contractCount() == contractCount + 1; } @Override public CompletableFuture<Void> doIt() { return licenseManager.contractInstance.createIssuerContract(itemName, textHash, url, lifeTime, price); } }); listContractData(null); } /** * List the LicenseMangager and data. * * @param contractAddress * @throws IOException * @throws InterruptedException * @throws ExecutionException */ public void listContractData(EthAddress contractAddress) throws IOException, InterruptedException, ExecutionException { System.out.println("\nLicensManager: " + licenseManager.contractInstance.issuerName()); System.out.println("Address: " + licenseManager.contractAddress); System.out.println("Payment Address: " + licenseManager.contractInstance.paymentAddress()); System.out.println("Contracts: " + licenseManager.contractInstance.contractCount() + " owner: " + licenseManager.contractInstance.owner()); for (int i = 0; i < licenseManager.contractInstance.contractCount(); i++) { EthAddress address = licenseManager.contractInstance.contracts(i); LicenseIssuer licenseIssuer = deployer.createLicenseIssuerProxy(sender, address); System.out.println(" --------------------------------- "); System.out.println(" License: " + licenseIssuer.licensedItemName()); System.out.println(" Price: " + licenseIssuer.licencePrice()); System.out.println(" Address: " + address); System.out.println(" Url: " + licenseIssuer.licenseUrl()); System.out.println(" text hash: " + licenseIssuer.licenseTextHash()); System.out .println(" issueable: " + licenseIssuer.issuable() + " " + licenseIssuer.licenseLifetime()); System.out.println(" LicenseCount: " + licenseIssuer.licenseCount()); for (int j = 0; j < licenseIssuer.licenseCount(); j++) { LicenseIssuerIssuedLicense issuedLicenses = licenseIssuer.issuedLicenses(j); System.out.println(" Issued License " + issuedLicenses.getLicenseOwnerName() + " " + issuedLicenses.getLicenseOwnerAdress() + " " + issuedLicenses.getIssuedDate()); } } } /** * Deploy a new License Manager. * * @param _paymentAddress * @param _name * @throws IOException * @throws InterruptedException * @throws ExecutionException */ public void deployLicenseManager(EthAddress _paymentAddress, String _name) throws IOException, InterruptedException, ExecutionException { licenseManager = deployer.createLicenseManager(sender, _paymentAddress, _name); listContractData(null); } private void setManager(EthAddress contractAddress) throws IOException, InterruptedException, ExecutionException { licenseManager = new DeployDuo<LicenseManager>(contractAddress, null); licenseManager.contractInstance = deployer.createLicenseManagerProxy(sender, contractAddress); } private void init(String senderKey, String senderPass) throws Exception { ethereum = EthereumInstance.getInstance().getEthereum(); String property = System.getProperty("EthereumFacadeProvider"); // testnetProvider if (property != null && (property.equalsIgnoreCase("rpc") || property.equalsIgnoreCase("ropsten") || property.equalsIgnoreCase("InfuraRopsten"))) { millis = 2000L; } else if (property != null && property.equalsIgnoreCase("private")) { sender = AccountProvider.fromPrivateKey(BigInteger.valueOf(100000L)); millis = 100L; } else { sender = AccountProvider.fromPrivateKey( (Hex.decode("3ec771c31cac8c0dba77a69e503765701d3c2bb62435888d4ffa38fed60c445c"))); millis = 10L; } if (senderKey != null && !senderKey.isEmpty() && sender == null) { sender = unlockAccount(senderKey, senderPass); } deployer = new ContractsDeployer(ethereum, "/contracts/combined.json", true); } /** * Unlocks an account. * * @param pathname * the key file * @param pass * the pass to unlocl * @return the account * @throws Exception */ private EthAccount unlockAccount(String pathname, String pass) throws Exception { SecureKey key2 = new FileSecureKey(new File(pathname)); System.out.println("unlock key: " + pathname); EthAccount decode = key2.decode(pass); String senderAddressS = decode.getAddress().withLeading0x(); EthValue balance = ethereum.getBalance(decode); System.out.println("Sender address and amount:" + senderAddressS + "->" + balance); return decode; } private void doAndWait(String message, DoAndWaitOneTime<?> action) throws InterruptedException, ExecutionException { System.out.println(message); doAndWait(action); } private void doAndWait(DoAndWaitOneTime<?> action) throws InterruptedException, ExecutionException { int timeOut = 0; if (!action.isDone()) { action.doIt().get(); while (!action.isDone() && timeOut++ < 200) synchronized (this) { System.out.print("."); wait(millis); } } System.out.println(); if (timeOut >= 200) System.out.println("Timeout:" + action); } private static Options createOptions() { Options options = new Options(); options.addOption(Option// .builder("de")// .desc("Don't exit the programm.")// .longOpt("dontExit")// .hasArg(false)// .build()); options.addOption(Option// .builder("calcDeploymendCost")// .desc("Calc the deployment cost.")// // .longOpt("calcDeploymendCost")// .hasArg(false)// .build()); options.addOption(Option// .builder("observeBlock")// .desc("Observes the blocks.")// // .longOpt("calcDeploymendCost")// .hasArg(false)// .build()); options.addOption(Option// .builder("f")// .desc("Set the contract source or the compiled json.")// .longOpt("file")// .hasArg(true)// .argName("file alreadyCompiled").numberOfArgs(2).build()); options.addOption(Option// .builder("sk")// .desc("Set the sender key file.")// .longOpt("senderKey")// .hasArg(true)// .argName("keyFile")// .numberOfArgs(1).build()); options.addOption(Option// .builder("sp")// .desc("Set the pass of the key of the sender.")// .longOpt("senderPass")// .hasArg(true)// .argName("password").numberOfArgs(1).build()); options.addOption(Option// .builder("millis")// .desc("The millisec to wait between checking the action.")// .hasArg(true)// .argName("millisec").numberOfArgs(1).build()); options.addOption(Option// .builder("wca")// .longOpt("writeContractAddress")// .desc("Write contract to file.")// .hasArg()// .argName("filename").numberOfArgs(1).build()); OptionGroup actionOptionGroup = new OptionGroup(); actionOptionGroup.setRequired(true); actionOptionGroup.addOption(Option.builder("h")// .longOpt("helps").desc("show help and usage")// .hasArg(false).build()); actionOptionGroup.addOption(Option.builder("c")// .desc("Deploys the contract on the blockchain").longOpt("create")// .hasArg(true)// .numberOfArgs(2)// .argName("paymenAddress, name")// .build()); actionOptionGroup.addOption(Option.builder("l")// .desc("List contract data")// .hasArg()// .argName("contractAddress")// .build()// ); actionOptionGroup.addOption(Option.builder("cic")// .desc("Create issuer contract. The price is in Finney")// .hasArg()// .numberOfArgs(6)// .argName("contractAddress, itemName, textHash, url, lifeTime, price")// .build()// ); actionOptionGroup.addOption(Option.builder("bli")// .desc("Buy license for address.")// .hasArg()// .numberOfArgs(2)// .argName("issuerAddress, name, address")// .build()// ); actionOptionGroup.addOption(Option.builder("v")// .desc("Verify a licence.")// .hasArg()// .numberOfArgs(4)// .argName("issuerAddress, message, signature, publicKey")// .build()// ); actionOptionGroup.addOption(Option.builder("cs")// .desc("Create a signature from a given text for the given Key.")// .hasArg()// .numberOfArgs(1)// .argName("message")// .build()// ); actionOptionGroup.addOption(Option.builder("co")// .longOpt("changeOwner")// .desc("Change owner")// .hasArg()// .numberOfArgs(2)// .argName("contractAddress newOwnerAddress")// .build()// ); actionOptionGroup.addOption(Option.builder("si")// .longOpt("stopIssuing")// .desc("Stop issuing license on this license isuer.")// .hasArg()// .numberOfArgs(1)// .argName("contractAddress")// .build()// ); actionOptionGroup.addOption(Option.builder("ppuk")// .longOpt("Print the public key.")// .desc("Stop issuing license on this license isuer.")// .build()// ); options.addOptionGroup(actionOptionGroup); return options; } /** * @param options */ private static void printHelp(Options options) { System.out.println("used EthereumFacadeProvider:" + System.getProperty("EthereumFacadeProvider") + "\n\n"); StringBuffer buffer = new StringBuffer(); buffer.append("change the ethereum client via -DEthereumFacadeProvider=<type>\n")// .append("type : main - the main net\n")// .append(" test - the test net\n")// .append(" ropsten - the ropsten net\n")// .append(" private - the private net\n")// .append(" InfuraRopsten - the ropsten net via Infrua\n")// .append(" InfuraMain - the main net via Infrua\n")// .append(" -DapiKey=<key> - the the api key for the service\n")// .append(" rpc - connect via rpc\n")// .append(" -Drpc-url=<url> - the url of the rpc server\n")// .append(" -Dchain-id=<id> - the chain id (0 for the main chain and 3 for ropsten)\n")// .append("\n"); HelpFormatter formatter = new HelpFormatter(); String header = "\nA deployer and manager for for a version database on the blockchain. (c) Urs Zeidler 2017n"; String footer = "\nexample: \n\n" + buffer.toString(); formatter.printHelp(150, "checksum database on the blockchain", header, options, footer, true); } // private static Byte[] toByteArray(byte[] byteArray) { // List<Byte> asList = Bytes.asList(byteArray); // return asList.toArray(new Byte[] {}); // } // public void setMillis(long millis) { this.millis = millis; } public DeployDuo<LicenseManager> getLicenseManager() { return licenseManager; } public void setSender(EthAccount sender) { this.sender = sender; } }