In allen vorigen Beispielen zur Bibliothek BitcoinJ haben wir uns mehr oder weniger mit der Abfrage von Daten aus dem Wallet beschäftigt. Jetzt möchten wir zum ersten Mal etwas mit unserem Guthaben im Wallet machen und eine richtige Transaktion, d.h. eine Überweisung, von Bitcoins auslösen.
Hierzu ist es notwendig, das Ihr Euch ein eigenes Wallet erzeugt und dieses Wallet mit (Testnetz-) Bitcoins füllt. Am einfachsten geht das mit einem Bitcoin Faucet und die Anleitung dazu findet Ihr im Artikel Bitcoin Anforderung von Testbitcoins. Die Sendeadresse (aus Sicht des Faucets) bzw. die Empfangsadresse (Eures Wallets) entnehmt Ihr z.B. aus der Ausgabe des Programms BitcoinJ 03 Daten aus einem Wallet auslesen (hier die Adresse aus der Zeile „Adresse für Zahlungseingänge“ kopieren). Kleiner Hinweis: bitte NICHT die unten stehende Adresse kopieren sonst erhalte ich bzw. mein Wallet die Bitcoins :-).
1 2 3 4 5 6 7 |
BitcoinJ 03 Daten eines Wallets Wir benutzen für unsere Versuche das Bitcoin Testnetz und nicht das Mainnetz Laden einer bestehenden Walletdatei - die Datei bitcoinj02createwalletW.wallet wird geladen ********************************************************************************** Ausgabe von Daten des Wallets Adresse für Zahlungseingänge: n3Wa5ZC1ihiR4BbUBU2tfq4A7v14kanTiq |
Wenn Euer Wallet nun ein Guthaben aufweist, seid Ihr bereit für eine Transaktion. Bitte beachtet aber, dass ein Guthaben erst nach der ersten Bestätigung für eine Transaktion zur Verfügung steht (eventuell kontrolliert Ihr den Status der Anforderungstransaktion mit einem Bitcoin Explorer).
Das nachfolgende Programm überweist den Betrag von 2.001 BTC (fest kodiert in Zeile 108) an eine ebenso feste Empfangsadresse (Zeile 104). Diese solltet Ihr ebenfalls auf eine eigene Adresse ändern, wie Ihr sie z.B. beim Online-Wallet vom Anbieter „block.io“ oder einem eigenen Electrum-Wallet erhaltet (Details hierzu in den jeweiligen Artikeln), sonst gehen auch diese Bitcoins an mein Wallet. Da dieser Überweisungsbetrag in der Regel zu hoch für das vorhandene Guthaben ist wird eine Fehlermeldung geben. Ändert Ihr nun den Betrag auf z.B. 0.001 BTC ab (abhängig von Eurem Guthaben und bitte auch an die Gebühren denken) wird die Transaktion ausgeführt.
Bei der Ausführung des Programms und Beobachtung einer Transaktion entstehen an 2 Stellen Pausen: zum einen aktualisiert das Programm beim Start des Wallet App Kits das Wallet und zum anderen fragt das Programm nach dem Absenden der Transaktion den aktuellen Status ab, bitte also nicht ungeduldig werden.
Ich habe dieses Programm mit einem zusätzlichen Service ausgestattet: Das Programm schreibt die Konsolenausgaben in eigene Fenster, und zwar eins für die Programmausgaben und ein zweites Fenster für die Ausgaben der Logfunktionen. Beide Fenster speichern parallel die Zeilen in eigene Dateien ab (Dateinamen: BitcoinJ06Transaction_Output_xxx.txt und BitcoinJ06Transaction_Logfile_xxx.txt, wobei „xxx“ für das aktuelle Datum und die Uhrzeit steht); so könnt Ihr die Ausgaben später ganz in Ruhe studieren.
Zur Nutzung benötigt Ihr diverse Bibliotheken zur Nutzung von BitcoinJ – ladet Euch diese aus dem separaten Github-Archiv (https://github.com/java-crypto/BitcoinJ_Libraries) herunter und bindet Sie über Eure Entwicklungsumgebung ein.
Es könnte auch noch ein anderer Codezeilen-Block Eure Aufmerksamkeit bekommen, nämlich die Zeilen 51 bis 73 und Zeilen 88 bis 90. Ich lege hier schon die Grundlage für die spätere Arbeit im „Regressions-Testnetzwerk“ (kurz „RegTest“ genannt) von Bitcoin. In weiteren Artikeln werde ich mehr dazu schreiben, hier nur eine kurze Vorabinformation. Sobald Ihr mit Transaktionen und den dazugehörenden Nachrichten und Folgeaktionen arbeitet werdet Ihr schnell merken, das Euch die Test-Bitcoins ganz schnell ausgehen werden. Das RegTest wird auf Eurem Rechner, also völlig lokal, erstellt und darin könnt Ihr Euch nach Belieben die Bitcoins zusenden und eigene Blöcke in der Blockchain generieren. Ebenso hört darin auch das Warten auf ein „Mining“ der Vergangenheit an, denn Ihr bestimmt den Zeitpunkt des Mining.
Hier nun der Sourcecode des Programms, der RedirectFrame-Klasse (am Artikelende) und die Programmausgabe im Konsolenfenster:
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
/* * Herkunft/Origin: http://javacrypto.bplaced.net/ * Programmierer/Programmer: Michael Fehr * Copyright/Copyright: Michael Fehr * Lizenttext/Licence: verschiedene Lizenzen / several licenses * getestet mit/tested with: Java Runtime Environment 11.0.5 x64 * verwendete IDE/used IDE: intelliJ IDEA 2019.3.1 * Datum/Date (dd.mm.jjjj): 12.03.2020 * Funktion: 06 Erzeugt eine Transaktion * Function: 06 creates an transaction * * Sicherheitshinweis/Security notice * Die Programmroutinen dienen nur der Darstellung und haben keinen Anspruch auf eine korrekte Funktion, * insbesondere mit Blick auf die Sicherheit ! * Prüfen Sie die Sicherheit bevor das Programm in der echten Welt eingesetzt wird. * The program routines just show the function but please be aware of the security part - * check yourself before using in the real world ! * * Sie benötigen diverse Bibliotheken (alle im Github-Archiv im Unterordner "libs") * You need a lot of libraries (see my Github-repository in subfolder "libs") * verwendete BitcoinJ-Bibliothek / used BitcoinJ Library: bitcoinj-core-0.15.6.jar * my Github-Repository: https://github.com/java-crypto/BitcoinJ * libs in my Github-Repo: https://github.com/java-crypto/BitcoinJ_Libraries * */ import org.bitcoinj.core.*; import org.bitcoinj.kits.WalletAppKit; import org.bitcoinj.params.RegTestParams; import org.bitcoinj.params.TestNet3Params; import org.bitcoinj.utils.BriefLogFormatter; import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.Wallet; import org.bitcoinj.wallet.WalletTransaction; import javax.swing.*; import java.io.File; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.concurrent.ExecutionException; public class BitcoinJ06CreateTransaction { public static void main(String[] args) throws AddressFormatException, ExecutionException, InterruptedException { // redirect the console to two windows RedirectedFrame outputFrameErrors = new RedirectedFrame("Logfileausgaben Fenster", true, false, true, "BitcoinJ06Transaction_Logfile_" + getActualDateReverse() + ".txt", 700, 600, JFrame.DO_NOTHING_ON_CLOSE); RedirectedFrame outputFrameOutput = new RedirectedFrame("Programmausgaben Fenster", false, true, true, "BitcoinJ06Transaction_Output_" + getActualDateReverse() + ".txt", 700, 600, JFrame.DO_NOTHING_ON_CLOSE); System.out.println("BitcoinJ 06 Erzeuge eine Transaction (Ueberweisung)"); NetworkParameters netParams = TestNet3Params.get(); // preset String filenameWallet = "bitcoinj02createwallet"; // choose network type (TEST or REG) String networkType = "TEST"; //String networkType = "REG"; switch (networkType) { case "TEST": { netParams = TestNet3Params.get(); //filenameWallet = filenameWallet + "_Testnet"; break; } case "REG": { netParams = RegTestParams.get(); //filenameWallet = filenameWallet + "_Regtest"; break; } default: { System.out.println("Es ist kein networkType angegeben, das Programm wird nicht gestartet"); JOptionPane.showMessageDialog(null, "Das Programm wird beendet sobald der OK-Button betaetigt wird", "Programmende", JOptionPane.WARNING_MESSAGE); System.exit(0); } } System.out.println("Das Programm arbeitet im Netzwerk: " + netParams.getId()); // init wallet app kit WalletAppKit kit = null; BriefLogFormatter.init(); kit = new WalletAppKit(netParams, new File("."), filenameWallet) { @Override protected void onSetupCompleted() { if (wallet().getKeyChainGroupSize() < 1) { wallet().importKey(new ECKey()); } } }; System.out.println("Startzeit des WalletAppKit : " + getActualDate()); System.out.println("Bitte warten, das Wallet App Kit geht online und aktualisiert das Wallet"); if (netParams == RegTestParams.get()) { kit.connectToLocalHost(); // neccessary for regtest-mode } kit.startAsync(); kit.awaitRunning(); // data from wallet String currentAddress = kit.wallet().currentReceiveAddress().toString(); String balanceAvailable = kit.wallet().getBalance(Wallet.BalanceType.AVAILABLE).toFriendlyString(); System.out.println("\nAktuelle Empfangsadresse : " + currentAddress); System.out.println("Balance/Guthaben verfuegbar : " + balanceAvailable + " (entspricht " + kit.wallet().getBalance(Wallet.BalanceType.AVAILABLE).multiply(1000).toPlainString() + " mBTC)"); System.out.println("Transactionen Pending : " + kit.wallet().getTransactionPool(WalletTransaction.Pool.PENDING).size()); System.out.println("Transactions Unspent : " + kit.wallet().getTransactionPool(WalletTransaction.Pool.UNSPENT).size()); System.out.println("Transactions Spent : " + kit.wallet().getTransactionPool(WalletTransaction.Pool.SPENT).size()); System.out.println("Transactions Dead : " + kit.wallet().getTransactionPool(WalletTransaction.Pool.DEAD).size()); // empfangsadresse = recipient address String recipient = "2MyEHka6Whdt87LyK3VuUGGfuWkNTtSD3Hj"; // block.io account Address recipientAddress = Address.fromString(netParams, recipient.trim()); System.out.println("Empfaengeradresse : " + recipientAddress); // tell peer to send amountToSend to recipientAddress Coin btcToSend = Coin.parseCoin("2.001"); System.out.println("Transaktionsbetrag: " + btcToSend.toFriendlyString() + " (entspricht " + btcToSend.multiply(1000).toPlainString() + " mBTC)" + " an diese Adresse: " + recipient); SendRequest request = SendRequest.to(recipientAddress, btcToSend); //request.recipientsPayFees = true; // recipient pays the fees means the fee is subtracted from amount that goes to the recipient Wallet.SendResult sendresult = null; // final confirmation String message = "Sollen " + btcToSend.toFriendlyString() + " (entspricht " + btcToSend.multiply(1000).toPlainString() + " mBTC)\n" + "an die Adresse " + recipient + "\nueberwiesen werden ?"; if (JOptionPane.showConfirmDialog(null, message, "Transaktionsbestaetigung", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { // yes option } else { // no option kit.stopAsync(); // wait for completion System.out.println("\nEndezeit des WalletAppKit : " + getActualDate()); JOptionPane.showMessageDialog(null, "Das Programm wird beendet sobald der OK-Button betaetigt wird", "Programmende", JOptionPane.WARNING_MESSAGE); outputFrameErrors.dispose(); outputFrameOutput.dispose(); return; } try { sendresult = kit.wallet().sendCoins(request); System.out.println("\nDie Transaktion wurde ausgefuehrt !"); System.out.println("Sendedatum: (request update time) : " + request.tx.getUpdateTime()); System.out.println("Transaktions-ID (sendresult TxId) : " + sendresult.tx.getTxId()); Coin txValue = request.tx.getValue(kit.wallet()); System.out.println("Transaktionsbetrag : " + txValue.toFriendlyString() + " (entspricht " + txValue.multiply(1000).toPlainString() + " mBTC)"); Coin fee = sendresult.broadcast.broadcast().get().getFee(); System.out.println("Gebuehr (sendresult broadcast fee) : " + fee.toFriendlyString() + " (entspricht " + fee.multiply(1000).toPlainString() + " mBTC)"); } catch (InsufficientMoneyException e) { System.out.println("\n* * * Fehler: Ungenuegendes Guthaben im Wallet, die Transaktion wurde nicht ausgefuehrt * * *"); JOptionPane.showMessageDialog(null, "* * * Fehler: Ungenuegendes Guthaben im Wallet, die Transaktion wurde nicht ausgefuehrt * * *", "Fehler", JOptionPane.WARNING_MESSAGE); e.printStackTrace(); } kit.stopAsync(); // wait for completion System.out.println("\nEndezeit des WalletAppKit : " + getActualDate()); JOptionPane.showMessageDialog(null, "Das Programm wird beendet sobald der OK-Button betaetigt wird", "Programmende", JOptionPane.WARNING_MESSAGE); outputFrameErrors.dispose(); outputFrameOutput.dispose(); } private static String getActualDateReverse() { // provides the actual date and time in this format yyyy-MM-dd_HH-mm-ss e.g. 2020-03-16_10-27-15 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"); LocalDateTime today = LocalDateTime.now(); return formatter.format(today); } private static String getActualDate() { // provides the actual date and time in this format dd.MM.yyyy HH:mm:ss e.g. 16.03.2020 10:27:15 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"); LocalDateTime today = LocalDateTime.now(); return formatter.format(today); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
BitcoinJ 06 Erzeuge eine Transaction (Ueberweisung) Das Programm arbeitet im Netzwerk: org.bitcoin.test Startzeit des WalletAppKit : 17.03.2020 16:33:53 Aktuelle Empfangsadresse : mg82tqqrRZfhuM6ordipcaoSiDXLigkiBj Balance/Guthaben verfuegbar : 0.02339727 BTC (entspricht 23.39727 mBTC) Transactionen Pending : 0 Transactions Unspent : 5 Transactions Spent : 17 Transactions Dead : 0 Empfaengeradresse : 2MyEHka6Whdt87LyK3VuUGGfuWkNTtSD3Hj Transaktionsbetrag: 0.001 BTC (entspricht 1 mBTC) an diese Adresse: 2MyEHka6Whdt87LyK3VuUGGfuWkNTtSD3Hj Die Transaktion wurde ausgefuehrt ! Sendedatum: (request update time) : Tue Mar 17 16:34:20 CET 2020 Transaktions-ID (sendresult TxId) : 4e7c11ec81b28a5adfb4807341df2647dde56121ecb4c106603e45b4066e2c18 Transaktionsbetrag : -0.001225 BTC (entspricht -1.225 mBTC) Gebuehr (sendresult broadcast fee) : 0.000225 BTC (entspricht 0.225 mBTC) Endezeit des WalletAppKit : 17.03.2020 16:34:35 |
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
import javax.swing.*; import java.awt.*; import java.io.*; /** * http://tanksoftware.com/juk/developer/src/com/ * tanksoftware/util/RedirectedFrame.java * A Java Swing class that captures output to the command line ** (eg, System.out.println) * RedirectedFrame * <p> * This class was downloaded from: * Java CodeGuru (http://codeguru.earthweb.com/java/articles/382.shtml) <br> * The origional author was Real Gagnon (real.gagnon@tactika.com); * William Denniss has edited the code, improving its customizability * * In breif, this class captures all output to the system and prints it in * a frame. You can choose weither or not you want to catch errors, log * them to a file and more. * For more details, read the constructor method description */ public class RedirectedFrame extends JFrame { // Class information public static final String PROGRAM_NAME = "Redirect Frame"; public static final String VERSION_NUMBER = "1.1"; public static final String DATE_UPDATED = "13 April 2001"; public static final String AUTHOR = "Real Gagnon - edited by William Denniss"; private boolean catchErrors; private boolean catchOutput; private boolean logFile; private String fileName; private int width; private int height; private int closeOperation; TextArea aTextArea = new TextArea(); PrintStream aPrintStream = new PrintStream( new FilteredStream( new ByteArrayOutputStream())); /** Creates a new RedirectFrame. * From the moment it is created, * all System.out messages and error messages (if requested) * are diverted to this frame and appended to the log file * (if requested) * * for example: * RedirectedFrame outputFrame = * new RedirectedFrame (false, false, null, 700, 600, JFrame.DO_NOTHING_ON_CLOSE); * this will create a new RedirectedFrame that doesn't catch errors, * nor logs to the file, with the dimentions 700x600 and it doesn't * close this frame can be toggled to visible, hidden by a controlling * class by(using the example) outputFrame.setVisible(true|false) * @param catchErrors set this to true if you want the errors to * also be caught * @param logFile set this to true if you want the output logged * @param fileName the name of the file it is to be logged to * @param width the width of the frame * @param height the height of the frame * @param closeOperation the default close operation * (this must be one of the WindowConstants) */ public RedirectedFrame(boolean catchErrors, boolean logFile, String fileName, int width, int height, int closeOperation) { this.catchErrors = catchErrors; this.logFile = logFile; this.fileName = fileName; this.width = width; this.height = height; this.closeOperation = closeOperation; Container c = getContentPane(); setTitle("Output Frame"); setSize(width,height); c.setLayout(new BorderLayout()); c.add("Center" , aTextArea); displayLog(); this.logFile = logFile; System.setOut(aPrintStream); // catches System.out messages if (catchErrors) System.setErr(aPrintStream); // catches error messages // set the default closing operation to the one given setDefaultCloseOperation(closeOperation); Toolkit tk = Toolkit.getDefaultToolkit(); Image im = tk.getImage("myicon.gif"); setIconImage(im); } public RedirectedFrame(String title, boolean catchErrors, boolean catchOutput, boolean logFile, String fileName, int width, int height, int closeOperation) { this.catchErrors = catchErrors; this.catchOutput = catchOutput; this.logFile = logFile; this.fileName = fileName; this.width = width; this.height = height; this.closeOperation = closeOperation; Container c = getContentPane(); //setTitle("Output Frame"); setTitle(title); setSize(width,height); c.setLayout(new BorderLayout()); c.add("Center" , aTextArea); displayLog(); this.logFile = logFile; if (catchOutput) System.setOut(aPrintStream); // catches System.out messages if (catchErrors) System.setErr(aPrintStream); // catches error messages // set the default closing operation to the one given setDefaultCloseOperation(closeOperation); Toolkit tk = Toolkit.getDefaultToolkit(); Image im = tk.getImage("myicon.gif"); setIconImage(im); } class FilteredStream extends FilterOutputStream { public FilteredStream(OutputStream aStream) { super(aStream); } public void write(byte b[]) throws IOException { String aString = new String(b); aTextArea.append(aString); } public void write(byte b[], int off, int len) throws IOException { String aString = new String(b , off , len); aTextArea.append(aString); if (logFile) { FileWriter aWriter = new FileWriter(fileName, true); aWriter.write(aString); aWriter.close(); } } } private void displayLog() { Dimension dim = getToolkit().getScreenSize(); Rectangle abounds = getBounds(); Dimension dd = getSize(); setLocation((dim.width - abounds.width) / 2, (dim.height - abounds.height) / 2); setVisible(true); requestFocus(); } } |
Ich habe zwei kleine Hilfsprogramme erstellt, die Eure Transaktionen aus dem Wallet auslesen und anzeigen: BitcoinJ 07 Zeige Transaktionen und BitcoinJ Zeige Transaktionen Tab.
Alle Quellcodes zum Bitcoin findet Ihr zum Download in meinem Github-Repository BitcoinJ, welches Ihr über diesen Link erreicht: https://github.com/java-crypto/BitcoinJ. Alle Programme sind unter Java 11 lauffähig (vermutlich auch unter Java 8) und wurden mit intelliJ IDEA entwickelt, welches für dieses Programm aber nicht notwendig ist.
Noch ein Wort zum Thema „Lizenz“: Die von mir erstellten Beispiele selber stehen unter der „Unlicense“-Lizenz, allerdings werden zur Laufzeit diverse Bibliotheken eingebunden, welche zum Teil ganz eigene Lizenzen mitbringen. Darauf kann ich in meinen Lizenzhinweisen nicht hinweisen.
Letzte Bearbeitung: 21.03.2020