Auf dieser Seite sind nur die Klassen-Quellcodes zu finden, die Hauptklasse ist im Artikel I09 Der JavaCryptoCoin komplett zu sehen.
Die Block-Klasse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
package net.bplaced.javacrypto.blockchain.step9; /* * Diese Klasse gehört zu JavaCryptoCoin.java * This class belongs to JavaCryptoCoin.java */ import java.io.Serializable; import java.util.ArrayList; import java.util.Date; public class Block implements Serializable { private static final long serialVersionUID = 1984888774694344646L; public String hash; public String previousHash; public String merkleRoot; public ArrayList<Transaction> transactions = new ArrayList<>(); public long timeStamp; public int nonce; public long mineStartTimeStamp; public long mineEndTimeStamp; public String difficultyString = ""; public boolean mined = false; public Block(String previousHash) { this.previousHash = previousHash; this.timeStamp = new Date().getTime(); this.hash = calculateHash(); } public String calculateHash() { String calculatedhash = StringUtil.generateSha256( previousHash + Long.toString(timeStamp) + Integer.toString(nonce) + merkleRoot + Long.toString(mineStartTimeStamp) ); return calculatedhash; } public void mineBlock(int difficulty) { mineStartTimeStamp = new Date().getTime(); merkleRoot = StringUtil.getMerkleRoot(transactions); String target = StringUtil.getDifficultyString(difficulty, merkleRoot); while (!hash.substring(0, difficulty).equals(target)) { nonce++; hash = calculateHash(); } mineEndTimeStamp = new Date().getTime(); difficultyString = target; mined = true; System.out.println("Block Mined!!! " + (mineEndTimeStamp - mineStartTimeStamp) + " ms: " + hash); } public boolean addTransaction(Transaction transaction) { if (transaction == null) { return false; } if ((!"0".equals(previousHash)) && !transaction.asReward) { if ((transaction.processTransaction() != true)) { System.out.println("Die Transaktion konnte nicht bearbeitet werden, der Vorgang wurde abgebrochen."); return false; } } transactions.add(transaction); System.out.println("Die Transaktion wurde dem Block erfolgreich hinzugefügt"); return true; } } |
Die FileUtil-Klasse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
package net.bplaced.javacrypto.blockchain.step9; /* * Diese Klasse gehört zu JavaCryptoCoin.java * This class belongs to JavaCryptoCoin.java */ import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class FileUtil { public static void saveObject(String filenameString, Object object) { try { FileOutputStream fo = new FileOutputStream(new File(filenameString)); ObjectOutputStream oo = new ObjectOutputStream(fo); oo.writeObject(object); oo.close(); fo.close(); } catch (FileNotFoundException e) { System.out.println("Datei nicht gefunden"); } catch (IOException e) { System.out.println("Fehler bei der Initialisierung des Streams"); } } public static Object loadObject(String filenameString) { Object object = null; try { FileInputStream fi = new FileInputStream(new File(filenameString)); ObjectInputStream oi = new ObjectInputStream(fi); object = (Object) oi.readObject(); oi.close(); fi.close(); } catch (FileNotFoundException e) { System.out.println("Datei nicht gefunden"); } catch (IOException e) { System.out.println("Fehler bei der Initialisierung des Streams"); } catch (ClassNotFoundException e) { e.printStackTrace(); } return object; } public static void saveJsonFile(String filenameString, String jsonString) throws IOException { BufferedWriter writer = new BufferedWriter(new FileWriter(filenameString)); writer.write(jsonString); writer.close(); } } |
Die StringUtil-Klasse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
package net.bplaced.javacrypto.blockchain.step9; /* * Diese Klasse gehört zu JavaCryptoCoin.java * This class belongs to JavaCryptoCoin.java */ import java.security.*; import java.util.ArrayList; import java.util.Base64; import com.google.gson.GsonBuilder; import java.io.UnsupportedEncodingException; import java.util.List; public class StringUtil { public static String generateSha256(String input) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(input.getBytes("UTF-8")); StringBuilder hexString = new StringBuilder(); for (int i = 0; i < hash.length; i++) { String hex = Integer.toHexString(0xff & hash[i]); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public static byte[] generateECDSASig(PrivateKey privateKey, String input) { Signature dsa; byte[] output = new byte[0]; try { dsa = Signature.getInstance("SHA256withECDSA"); dsa.initSign(privateKey); byte[] strByte = input.getBytes(); dsa.update(strByte); byte[] realSig = dsa.sign(); output = realSig; } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException e) { throw new RuntimeException(e); } return output; } public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) { try { Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA"); ecdsaVerify.initVerify(publicKey); ecdsaVerify.update(data.getBytes()); return ecdsaVerify.verify(signature); } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException e) { throw new RuntimeException(e); } } public static String getJson(Object o) { return new GsonBuilder().setPrettyPrinting().create().toJson(o); } public static String getDifficultyString(int difficulty, String merkleRoot) { String s = ""; for(char c : merkleRoot.toCharArray()) { if(Character.isDigit(c)) s = s + c; } if(s.equals("") || s.length() <= 5 ) s = "0123456789"; String ret = ""; for(int i = 1; i <= difficulty; i++) { int n = difficulty * i; while(n >= s.length()) { n = (n - s.length()) + i; } if(n < 0) n = 0; ret = ret + s.substring(n, n + 1); } return ret; } public static String getStringFromKey(Key key) { return Base64.getEncoder().encodeToString(key.getEncoded()); } public static String getMerkleRoot(ArrayList<Transaction> transactions) { int count = transactions.size(); List<String> previousTreeLayer = new ArrayList<>(); for (Transaction transaction : transactions) { previousTreeLayer.add(transaction.transactionId); } List<String> treeLayer = previousTreeLayer; while (count > 1) { treeLayer = new ArrayList<>(); for (int i = 1; i < previousTreeLayer.size(); i += 2) { treeLayer.add(generateSha256(previousTreeLayer.get(i - 1) + previousTreeLayer.get(i))); } count = treeLayer.size(); previousTreeLayer = treeLayer; } String merkleRoot = (treeLayer.size() == 1) ? treeLayer.get(0) : ""; return merkleRoot; } } |
Die Transaction-Klasse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
package net.bplaced.javacrypto.blockchain.step9; /* * Diese Klasse gehört zu JavaCryptoCoin.java * This class belongs to JavaCryptoCoin.java */ import java.io.Serializable; import java.security.*; import java.util.ArrayList; public class Transaction implements Serializable{ private static final long serialVersionUID = -3795222285878701219L; public String transactionId; public PublicKey sender; public PublicKey recipient; public float value; public byte[] signature; public boolean asReward = false; public ArrayList<TransactionInput> inputs = new ArrayList<>(); public ArrayList<TransactionOutput> outputs = new ArrayList<>(); private static int sequence = 0; public Transaction(PublicKey from, PublicKey to, float value, ArrayList<TransactionInput> inputs) { this.sender = from; this.recipient = to; this.value = value; this.inputs = inputs; } public boolean processTransaction() { if (verifySignature() == false) { System.out.println("# Transaktion - Die Signatur ist nicht korrekt"); return false; } for (TransactionInput i : inputs) { i.UTXO = JavaCryptoCoin.UTXOs.get(i.transactionOutputId); } if (getInputsValue() < JavaCryptoCoin.minimumTransaction) { System.out.println("# Transaktion Der Input ist zu gering: " + getInputsValue()); System.out.println("# Bitte geben Sie einen Betrag groesser als " + JavaCryptoCoin.minimumTransaction + " ein."); return false; } float leftOver = getInputsValue() - value; //get value of inputs then the left over change: transactionId = calculateHash(); outputs.add(new TransactionOutput(this.recipient, value, transactionId)); //send value to recipient outputs.add(new TransactionOutput(this.sender, leftOver, transactionId)); //send the left over 'change' back to sender for (TransactionOutput o : outputs) { JavaCryptoCoin.UTXOs.put(o.id, o); } for (TransactionInput i : inputs) { if (i.UTXO == null) { continue; } JavaCryptoCoin.UTXOs.remove(i.UTXO.id); } return true; } public float getInputsValue() { float total = 0; for (TransactionInput i : inputs) { if (i.UTXO == null) { continue; } total += i.UTXO.value; } return total; } public void generateSignature(PrivateKey privateKey) { String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(recipient) + Float.toString(value); signature = StringUtil.generateECDSASig(privateKey, data); } public boolean verifySignature() { String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(recipient) + Float.toString(value); return StringUtil.verifyECDSASig(sender, data, signature); } public float getOutputsValue() { float total = 0; for (TransactionOutput o : outputs) { total += o.value; } return total; } public String calculateHash() { sequence++; return StringUtil.generateSha256( StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(recipient) + Float.toString(value) + sequence ); } } |
Die TransactionInput-Klasse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package net.bplaced.javacrypto.blockchain.step9; /* * Diese Klasse gehört zu JavaCryptoCoin.java * This class belongs to JavaCryptoCoin.java */ import java.io.Serializable; public class TransactionInput implements Serializable{ private static final long serialVersionUID = -6643746839251237698L; public String transactionOutputId; public TransactionOutput UTXO; public TransactionInput(String transactionOutputId) { this.transactionOutputId = transactionOutputId; } } |
Die TransactionOutput-Klasse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package net.bplaced.javacrypto.blockchain.step9; /* * Diese Klasse gehört zu JavaCryptoCoin.java * This class belongs to JavaCryptoCoin.java */ import java.io.Serializable; import java.security.PublicKey; public class TransactionOutput implements Serializable{ private static final long serialVersionUID = 7902714355455361435L; public String id; public PublicKey recipient; public float value; public String parentTransactionId; public TransactionOutput(PublicKey recipient, float value, String parentTransactionId) { this.recipient = recipient; this.value = value; this.parentTransactionId = parentTransactionId; this.id = StringUtil .generateSha256(StringUtil.getStringFromKey(recipient) + Float.toString(value) + parentTransactionId); } public boolean isMine(PublicKey publicKey) { return (publicKey.equals(recipient)); } } |
Die Wallet-Klasse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
package net.bplaced.javacrypto.blockchain.step9; /* * Diese Klasse gehört zu JavaCryptoCoin.java * This class belongs to JavaCryptoCoin.java */ import java.io.Serializable; import java.security.*; import java.security.spec.ECGenParameterSpec; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class Wallet implements Serializable{ private static final long serialVersionUID = 3090822928966119493L; public PrivateKey privateKey; public PublicKey publicKey; public HashMap<String, TransactionOutput> UTXOs = new HashMap<>(); public Wallet() { generateKeyPair(); } public void generateKeyPair() { try { // secp192r1 [NIST P-192, X9.62 prime192v1] (1.2.840.10045.3.1.1) // secp224k1 (1.3.132.0.32) // secp224r1 [NIST P-224] (1.3.132.0.33) // secp256k1 (1.3.132.0.10) // secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7) KeyPairGenerator keypairGenerator = KeyPairGenerator.getInstance("EC", "SunEC"); // ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp192r1"); ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1"); keypairGenerator.initialize(ecSpec, new SecureRandom()); KeyPair keyPair = keypairGenerator.genKeyPair(); privateKey = keyPair.getPrivate(); publicKey = keyPair.getPublic(); } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchProviderException e) { throw new RuntimeException(e); } } public float getBalance() { float total = 0; for (Map.Entry<String, TransactionOutput> item : JavaCryptoCoin.UTXOs.entrySet()) { TransactionOutput UTXO = item.getValue(); if (UTXO.isMine(publicKey)) { //if output belongs to me ( if coins belong to me ) UTXOs.put(UTXO.id, UTXO); //add it to our list of unspent transactions. total += UTXO.value; } } return total; } public Transaction sendFunds(PublicKey _recipient, float value) { if (getBalance() < value) { System.out.println("# Das Guthaben ist zu gering um die Transaktion auszuführen, die Transaktion wurde abgebrochen."); System.out.println("# Diese Transaktion scheitert: Empfänger:" + _recipient + "\n Überweisungsbetrag:" + value); return null; } ArrayList<TransactionInput> inputs = new ArrayList<>(); float total = 0; for (Map.Entry<String, TransactionOutput> item : UTXOs.entrySet()) { TransactionOutput UTXO = item.getValue(); total += UTXO.value; inputs.add(new TransactionInput(UTXO.id)); if (total > value) { break; } } Transaction newTransaction = new Transaction(publicKey, _recipient, value, inputs); newTransaction.generateSignature(privateKey); for (TransactionInput input : inputs) { UTXOs.remove(input.transactionOutputId); } return newTransaction; } } |
Hier geht es zurück zum Haupt-Teil 9: I09 Der JavaCryptoCoin komplett
Alle Quellcodes zur Blockchain findet Ihr zum Download in meinem Github-Repository, welches Ihr über diesen Link erreicht: https://github.com/java-crypto/I-Blockchain. Alle Programme sind sowohl unter Java 8 als auch unter Java 11 lauffähig.
Die Lizenz zum obigen Beispiel findet Ihr auf der eigenen Lizenz-Seite.
Letzte Aktualisierung: 15.06.2019