Blockchain bezeichnet eine kontinuierlich erweiterbare Liste von Datensätzen, welche mittels kryptographischer Verfahren miteinander verkettet sind, so dass die Kette der Datensätze unveränderlich und fälschungssicher ist. Neue Transaktionen werden validiert und in Blöcke verpackt, und die Blöcke werden nach Durchlaufen eines Konsensus-Algorithmus an die Blockchain angehängt und an alle anderen Blockchain-Server gesendet.
Ethereum
basiert auf einer öffentlichen Blockchain,
die in einem dezentralen Peer-to-Peer-Netz auf vielen Ethereum-Servern als
DLT betrieben wird.
Ethereum
beinhaltet die
Kryptowährung
Ether
und ermöglicht darüber hinaus
"Smart Contracts".
Damit können Verträge programmiert werden, die elektronisch ausgeführt und überprüft werden.
Dazu werden Skripte erstellt (meistens in der Programmiersprache
Solidity)
und in der Ethereum Virtual Machine (EVM) ausgeführt.
Dies eröffnet sehr vielfältige Möglichkeiten, beispielsweise basieren viele
ICOs darauf.
Allerdings muss bei der Programmierung von Smart Contracts besondere Vorsicht gelten:
Einprogrammierte Sicherheitslücken können fatale Folgen haben, wie beim
"The DAO Hack",
der zum Ethereum-Hard-Fork "Ethereum Classic" führte.
Grundsätzliche Erläuterungen gibt es unter: Kryptowährungen, Bitcoin, Ethereum, Blockchain. Im Folgenden werden einige einfache Programmierbeispiele für Smart Contracts gezeigt.
Diese Demo zeigt:
Wie "geth" (Go Ethereum) installiert wird.
Wie "solc" (Solidity Compiler) installiert wird.
Wie eine eigene private Test-Ethereum-Blockchain eingerichtet und gestartet wird.
Wie ein Account angelegt und Mining gestartet wird.
Verwendet werden folgende Versionen:
geth 1.8.2
solc 0.4.19
web3 0.20.1
Windows 10
Der folgende Text legt den Fokus auf Einfachheit und gute Nachvollziehbarkeit. Vorläufig werden nur Kommandozeilen-Tools eingesetzt. Auf grafische Tools wird weiter unten eingegangen.
In diesem Beispiel wird eine private nur auf dem eigenen PC existierende Ethereum-Blockchain eingerichtet. Wie eine öffentliche Ethereum-Blockchain verwendet werden kann, wird weiter unten gezeigt.
Die Kommandos sind für Windows dargestellt. Bei Verwendung von Linux oder Mac OS X genügt es häufig, in Pfadangaben "\" durch "/", in PATH-Angaben ";" durch ":" und bei Platzhaltern %MEINE_VARIABLE% durch $MEINE_VARIABLE zu ersetzen.
Führen Sie die im Folgenden beschriebenen Schritte aus.
Wechseln Sie in Ihr bevorzugtes Workspace-Verzeichnis (z.B. \MeinWorkspace) und führen Sie folgende Kommandos aus:
cd \MeinWorkspace
mkdir EthereumDemo
cd EthereumDemo
mkdir solc
mkdir src
tree /F
Installieren Sie solc (Solidity Compiler):
Downloaden Sie von
https://github.com/ethereum/solidity/releases
die für Ihr Betriebssystem geeignete Installationsdatei, beispielsweise für Windows:
solidity-windows.zip.
Unter Windows entzippen Sie diese Zip-Datei in das Verzeichnis:
\MeinWorkspace\EthereumDemo\solc.
Für andere Betriebssysteme verfahren Sie entweder analog oder wie beschrieben unter:
Installing the Solidity Compiler.
Führen Sie im Kommandozeilenfenster aus:
cd \MeinWorkspace\EthereumDemo
solc\solc.exe --version
solc\solc.exe --help
Beide Kommandos müssen plausible Ergebnisse zeigen.
Installieren Sie geth (Go Ethereum):
Downloaden Sie von
https://geth.ethereum.org/downloads/
eine für Ihr Betriebssystem geeignete Geth-Version, beispielsweise für Windows "Geth 1.8.2 for Windows".
Verwenden Sie nicht die Geth-Versionen 1.8.0 und 1.8.1, weil es damit zum
Web3j Issue 318 kommt.
Für Windows erhalten Sie die Datei "geth-windows-amd64-1.8.2-b8b9f7f4.exe".
Führen Sie diese Datei aus. Die Installation erweitert den Windows-Such-PATH um das Geth-Verzeichnis.
Für andere Betriebssysteme verfahren Sie wie beschrieben unter:
https://www.ethereum.org/cli oder
https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum.
Öffnen Sie ein neues Kommandozeilenfenster, damit Konfigurationsänderungen und PATH-Erweiterungen wirksam werden. Führen Sie darin aus:
cd \MeinWorkspace\EthereumDemo
geth version
geth help
Beide Kommandos müssen plausible Ergebnisse zeigen.
Zu den Kommandozeilenoptionen von geth finden Sie auch eine Auflistung unter: Command Line Options.
Genesis-Block:
Um eine eigene private Test-Ethereum-Blockchain zu starten,
wird eine manuelle Initialisierung des ersten Genesis-Blocks benötigt.
Erstellen Sie im src-Unterverzeichnis die JSON-Datei:
genesis-block.json
{ "alloc": {}, "coinbase": "0x0000000000000000000000000000000000000000", "config": {}, "difficulty": "0x4000", "extraData": "0x4711", "gasLimit": "0xffffffff", "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000042", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp": "0x00" }
Achten Sie beim Speichern darauf, dass die Datei mit Unix-Zeilenendezeichen und als ASCII-Datei ohne UTF-BOM gespeichert werden muss.
Einige Hinweise zum Genesis-Block finden Sie unter: Creating The Genesis Block.
Erstellen Sie den Genesis-Block, indem Sie ausführen:
cd \MeinWorkspace\EthereumDemo
geth --datadir "Private-Blockchain" init src/genesis-block.json
Sie erhalten u.a.:
Successfully wrote genesis state
Start der eigenen Test-Ethereum-Blockchain (vorerst ohne Mining):
Führen Sie aus (in einer Zeile):
geth --networkid 77 --identity "MeineDevChain" --datadir "Private-Blockchain" --nodiscover --rpc --rpcapi "db,eth,net,web3,personal,txpool" --rpccorsdomain "*" console
Sie erhalten u.a.:
Starting peer-to-peer node HTTP endpoint opened: http://127.0.0.1:8545 Welcome to the Geth JavaScript console!
Und Sie erhalten die Geth-JavaScript-Console, in der Sie Geth-JavaScript-Kommandos eingeben können.
Sehen Sie sich die Doku zu den web3.eth-Kommandos an: web3.eth 1.0 bzw. Web3 0.20.x JavaScript API. Fragen Sie die von Ihnen verwendete Web3.js-Version ab:
web3.version.api
Testen Sie, ob bereits Accounts existieren. Falls das Einfügen von Kommandos per Strg+V nicht funktioniert, versuchen Sie die rechte Maustaste, "Einfügen" und Return:
web3.eth.accounts
Sie erhalten eine leere Menge:
[]
Account anlegen:
Legen Sie einen Account an (denken Sie sich ein schwierigeres Passwort aus und merken Sie es sich gut):
personal.newAccount( "Meine Ethereum-Test-Passphrase" )
Sie erhalten eine 40-stellige Hex-Zahl als Account-Adresse, beispielsweise:
"0x2f94831a57a96041064d9d0c24583b12f807f2a5"
Testen Sie erneut auf vorhandene Accounts:
web3.eth.accounts
Diesmal erhalten Sie beispielsweise:
["0x2f94831a57a96041064d9d0c24583b12f807f2a5"]
Kontostände abfragen:
Erstellen Sie im src-Unterverzeichnis die JavaScript-Datei:
checkAllBalances.js
function checkAllBalances() { web3.eth.getAccounts( function( err, accounts ) { accounts.forEach( function( id ) { web3.eth.getBalance( id, function( err, balance ) { console.log( "" + id + ":\tbalance: " + web3.fromWei( balance, "ether" ) + " ether" ); } ); } ); } ); }; checkAllBalances()
Führen Sie in der Geth-JavaScript-Console aus:
loadScript( 'src/checkAllBalances.js' )
Sie erhalten:
0x2f94831a57a96041064d9d0c24583b12f807f2a5: balance: 0 ether
Mining manuell starten:
Führen Sie aus (führen Sie die einzelnen JavaScript-Kommandos nicht zu schnell hintereinander aus):
miner.setEtherbase( web3.eth.accounts[0] )
miner.start( 1 )
Sie erhalten eine nicht endende Ausgabe von Mining-Kommandos:
Starting mining operation Commit new mining work Successfully sealed new block mined potential block ...
Falls bei Ihnen das Mining nicht startet: Manchmal kommt es vor, dass das Mining erst nach vielleicht 10 Minuten beginnt.
Geben Sie dem Mining etwas Zeit, ca. eine Minute. Anschließend befinden sich auf dem Account Ether.
Beenden Sie die Test-Ethereum-Blockchain:
exit
Start-Skript:
Erstellen Sie im EthereumDemo-Projektverzeichnis die Batchdatei
starte-Test-Ethereum-Blockchain.bat
mit folgendem Inhalt (in einer Zeile):
geth --networkid 77 --identity "MeineDevChain" --datadir "Private-Blockchain" --nodiscover --rpc --rpcapi "db,eth,net,web3,personal,txpool" --rpccorsdomain "*" --mine --minerthreads 1 console 2>>priv-geth.log
Beachten Sie, dass diesmal per "--mine --minerthreads 1" das Mining automatisch aktiviert wird, und per "2>>priv-geth.log" die vielen Ausgaben in eine Logdatei umgeleitet werden.
Start der eigenen Test-Ethereum-Blockchain inklusive Mining:
Führen Sie aus:
cd \MeinWorkspace\EthereumDemo
starte-Test-Ethereum-Blockchain.bat
Sie erhalten z.B.:
Welcome to the Geth JavaScript console! instance: Geth/MeineDevChain/v1.8.2-stable-b8b9f7f4/windows-amd64/go1.9 coinbase: 0x2f94831a57a96041064d9d0c24583b12f807f2a5 at block: 1448 (Mon, 22 Jan 2018 12:53:13 CET) datadir: \MeinWorkspace\EthereumDemo\Private-Blockchain modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
Führen Sie in der Geth-JavaScript-Console erneut aus:
loadScript( 'src/checkAllBalances.js' )
Diesmal erhalten Sie einen positiven Betrag, beispielsweise:
0x2f94831a57a96041064d9d0c24583b12f807f2a5: balance: 4711 ether
Führen Sie zum Kennenlernen weitere Kommandos aus, beispielsweise:
admin.datadir
admin.nodeInfo
eth
eth.getBlock( "latest" )
miner.getHashrate()
personal.listAccounts
txpool.content
web3
Sehen Sie sich die Erläuterungen zu den Management-Kommandos an.
Beenden Sie die Test-Ethereum-Blockchain:
exit
Überprüfung der Keystore-Datei und der Verzeichnisstruktur:
Führen Sie aus:
dir \MeinWorkspace\EthereumDemo\Private-Blockchain\keystore
Dort befindet sich die Keystore-Datei, beispielsweise:
UTC--2018-01-22T09-50-58.520494400Z--2f94831a57a96041064d9d0c24583b12f807f2a5
Die Projektstruktur sieht jetzt so aus (überprüfen Sie es unter Windows mit tree /F und unter Linux mit tree):
[\MeinWorkspace\EthereumDemo] |- priv-geth.log |- starte-Test-Ethereum-Blockchain.bat |- [Private-Blockchain] | |- history | |- [geth] | | '- ... | '- [keystore] | '- UTC--2018-01-22T09-50-58... |- [solc] | |- msvcp140.dll | |- solc.exe | '- soltest.exe '- [src] |- checkAllBalances.js '- genesis-block.json
Diese Demo zeigt:
Wie ein einfaches Solidity-Skript für einen Ethereum Smart Contract aussehen kann.
Wie das Solidity-Skript kompiliert und in die Ethereum-Blockchain deployt wird.
Wie der Ethereum Smart Contract ausgeführt wird.
Auch in dieser Demo werden noch nur Kommandozeilen-Tools eingesetzt. Auf grafische Tools wird weiter unten eingegangen.
Führen Sie die im Folgenden beschriebenen Schritte aus.
Alle oben unter Basisinstallationen: Geth, Solc, private Test-Ethereum-Blockchain beschriebenen Schritte müssen ausgeführt worden sein.
Wechseln Sie in Ihr oben eingerichtetes EthereumDemo-Workspace-Verzeichnis (z.B. \MeinWorkspace\EthereumDemo):
cd \MeinWorkspace\EthereumDemo
Erstellen Sie im src-Unterverzeichnis das
Solidity-Skript:
Greeter.sol
pragma solidity ^0.4.19; /* Hilfsklasse, damit der Smart Contract nach Benutzung entfernt werden kann */ contract Mortal { /* Besitzer */ address owner; /* Konstruktor, wird bei der Initialisierung aufgerufen */ function Mortal() public { owner = msg.sender; } /* Entfernen des Contracts */ function kill() public { if( msg.sender == owner ) selfdestruct( owner ); } } /* Hauptanwendungsklasse, abgeleitet von Mortal */ contract Greeter is Mortal { /* Zu speichernder Text */ string greeting; /* Konstruktor, wird bei der Initialisierung aufgerufen */ function Greeter( string _greeting ) public { greeting = _greeting; } /* Hauptanwendungsmethode, Wiedergabe des gespeicherten Texts */ function greet() public constant returns( string ) { return greeting; } }
Dies ist eine geringfügig erweiterte Version der Demo unter The Greeter. Dort finden Sie auch Kommentare zum Solidity-Sourcecode.
Kompilieren Sie das Solidity-Skript:
cd \MeinWorkspace\EthereumDemo
solc\solc -o target --bin --abi --overwrite src/Greeter.sol
dir target
Sie erhalten das neue Verzeichnis "target" mit vier neuen Dateien:
[\MeinWorkspace\EthereumDemo] |- priv-geth.log |- starte-Test-Ethereum-Blockchain.bat |- [Private-Blockchain] | '- ... |- [solc] | '- ... |- [src] | |- checkAllBalances.js | |- genesis-block.json | '- Greeter.sol '- [target] |- Greeter.abi |- Greeter.bin |- Mortal.abi '- Mortal.bin
Um diesen kompilierten Ethereum Smart Contract in die Ethereum-Blockchain zu deployen, wird eine JavaScript-Datei erstellt, welche mit den zwei folgenden Kommandos beginnt:
var greeterFactory = eth.contract( {Inhalt der Datei target/Greeter.abi} ) var greeterCompiled = "0x" + "{Inhalt der Datei target/Greeter.bin}" ...
Dabei müssen die beiden Platzhalterausdrücke "{...}" durch die jeweiligen Dateinhalte ersetzt werden.
Unter Windows erstellen Sie hierzu im src-Verzeichnis folgende drei Dateien, jeweils ohne Zeilenendezeichen am Ende:
greeterJsTeil1.txt
var greeterFactory = eth.contract(
greeterJsTeil2.txt
) var greeterCompiled = "0x" + "
greeterJsTeil3.txt
" var _greeting = "Hallo Welt!" var Greeter = greeterFactory.new( _greeting, { from:eth.accounts[0], data:greeterCompiled, gas:47000000 }, function( e, contract ) { if( e ) { console.log( e ); } else { if( !contract.address ) { console.log( "Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined..." ); } else { console.log( "Contract mined! Address: " + contract.address ); console.log( contract ); } } })
Anschließend führen Sie aus:
copy /B /Y src\greeterJsTeil1.txt + target\Greeter.abi + src\greeterJsTeil2.txt + target\Greeter.bin + src\greeterJsTeil3.txt src\GreeterDeploy.js
type src\GreeterDeploy.js
Alternativ könnten Sie den obigen Solidity-Sourcecode auch in den Remix Online Solidity Compiler unter http://remix.ethereum.org kopieren, "Start to compile" und "Details" betätigen, und das JavaScript unter "WEB3DEPLOY" in die Datei "src\GreeterDeploy.js" kopieren.
Die Projektstruktur sieht jetzt so aus:
[\MeinWorkspace\EthereumDemo] |- priv-geth.log |- starte-Test-Ethereum-Blockchain.bat |- [Private-Blockchain] | |- history | |- [geth] | | '- ... | '- [keystore] | '- UTC--2018-01-22T09-50-58... |- [solc] | |- msvcp140.dll | |- solc.exe | '- soltest.exe |- [src] | |- checkAllBalances.js | |- genesis-block.json | '- Greeter.sol | |- GreeterDeploy.js | |- greeterJsTeil1.txt | |- greeterJsTeil2.txt | |- greeterJsTeil3.txt '- [target] |- Greeter.abi |- Greeter.bin |- Mortal.abi '- Mortal.bin
Starten Sie die Test-Ethereum-Blockchain inklusive Mining:
cd \MeinWorkspace\EthereumDemo
starte-Test-Ethereum-Blockchain.bat
Führen Sie in der Geth-JavaScript-Console folgende drei Kommandos aus und überprüfen Sie jeweils das Ergebnis (die beiden loadScript()-Antworttexte stammen aus dem obigen GreeterDeploy.js-JavaScript-Code, und die Greeter.greet()-Antwort stammt vom Smart Contract):
personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" )
true
loadScript( 'src/GreeterDeploy.js' )
Contract transaction send: TransactionHash: 0xe194...7dad waiting to be mined... true ... Contract mined! Address: 0xcd5e3f508e24fb8424ea38425f3f0c860b52372d
Greeter.greet();
"Hallo Welt!"
Damit haben Sie den Ethereum Smart Contract erfolgreich ausgeführt.
Da das Solidity-Skript eine kill()-Methode hat, kann es folgendermaßen aus der Blockchain entfernt werden:
personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" )
Greeter.kill.sendTransaction( { from:eth.accounts[0] } )
Die Kontrolle erfolgt über:
eth.getCode( Greeter.address )
Sie erhalten:
"0x"
Diese Demo zeigt:
Einen Smart Contract für ein eigenes Token (welches eine eigene Kryptowährung repräsentieren könnte), allerdings nur in einer sehr stark vereinfachten Variante, die nur das Grundprinzip verdeutlicht.
Eine wesentlich vollständigere und umfangreichere ERC20-Token-Demo finden Sie unter: Contract TokenERC20.
Die Umsetzung erfolgt vorerst mit den Kommandozeilen-Tools Geth und Solc. Weiter unten wird dieselbe Demo mit den grafischen Tools Remix Online Solidity IDE und Mist implementiert und verwendet. Noch weiter unten wird die Demo mit den Entwicklungsframeworks Truffle und Embark implementiert und verwendet.
Vorerst wird der Smart Contract in der eigenen privaten Blockchain deployt und ausgeführt. Weiter unten erfolgt dies in der öffentlichen Rinkeby-Blockchain.
Führen Sie die im Folgenden beschriebenen Schritte aus.
Alle oben unter Basisinstallationen: Geth, Solc, private Test-Ethereum-Blockchain beschriebenen Schritte müssen ausgeführt worden sein.
Wechseln Sie in Ihr vorbereitetes EthereumDemo-Workspace-Verzeichnis (z.B. \MeinWorkspace\EthereumDemo):
cd \MeinWorkspace\EthereumDemo
Erstellen Sie im src-Unterverzeichnis das Solidity-Skript:
MeinToken.sol
pragma solidity ^0.4.19; contract MeinToken { /* Map mit Account-Adressen und dazugehoerenden Kontostaenden */ mapping( address => uint256 ) public balanceOf; /* Konstruktor, wird bei der Initialisierung aufgerufen, uebertraegt initiale Menge an Tokens an den Ersteller des Smart Contracts */ function MeinToken( uint256 initialSupply ) public { balanceOf[msg.sender] = initialSupply; } /* Transferiere Tokens */ function transfer( address _to, uint256 _value ) public { require( msg.sender != _to ); require( balanceOf[msg.sender] >= _value ); require( balanceOf[_to] + _value >= balanceOf[_to] ); balanceOf[msg.sender] -= _value; balanceOf[_to] += _value; } }
Erstellen Sie im src-Unterverzeichnis folgende drei JavaScript-Fragmente, jeweils ohne Zeilenendezeichen am Ende.
meinTokenJsTeil1.txt
var meinTokenFactory = eth.contract(
meinTokenJsTeil2.txt
) var meinTokenCompiled = "0x" + "
meinTokenJsTeil3.txt
" var initialSupply = 888 var gasOffer = eth.getBlock( "latest" ).gasLimit / 2 var MeinToken = meinTokenFactory.new( initialSupply, { from:eth.accounts[0], data:meinTokenCompiled, gas:gasOffer }, function( e, contract ) { if( e ) { console.log( e ); } else { if( !contract.address ) { console.log( "Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined..." ); } else { console.log( "Contract mined! Address: " + contract.address ); console.log( contract ); } } })
Erstellen Sie im EthereumDemo-Projektverzeichnis die Batchdatei:
build-MeinToken.bat
solc\solc -o target --bin --abi --overwrite src/MeinToken.sol dir target copy /B /Y src\meinTokenJsTeil1.txt + target\MeinToken.abi + src\meinTokenJsTeil2.txt + ^ target\MeinToken.bin + src\meinTokenJsTeil3.txt src\MeinTokenDeploy.js type src\MeinTokenDeploy.js
Führen Sie folgende Kommandos aus:
cd \MeinWorkspace\EthereumDemo
build-MeinToken.bat
tree /F
Die Projektstruktur sieht jetzt so aus (falls Sie auch die obige Erster Smart Contract: Hallo-Welt-Demo ausgeführt haben, haben Sie ein paar zusätzliche Dateien, die aber nicht stören):
[\MeinWorkspace\EthereumDemo] |- build-MeinToken.bat |- priv-geth.log |- starte-Test-Ethereum-Blockchain.bat |- [Private-Blockchain] | |- history | |- [geth] | | '- ... | '- [keystore] | '- UTC--2018-01-22T09-50-58... |- [solc] | |- msvcp140.dll | |- solc.exe | '- soltest.exe |- [src] | |- checkAllBalances.js | |- genesis-block.json | '- MeinToken.sol | |- MeinTokenDeploy.js | |- meinTokenJsTeil1.txt | |- meinTokenJsTeil2.txt | '- meinTokenJsTeil3.txt '- [target] |- MeinToken.abi '- MeinToken.bin
Starten Sie die Blockchain:
cd \MeinWorkspace\EthereumDemo
starte-Test-Ethereum-Blockchain.bat
Führen Sie in der Geth-JavaScript-Console aus (führen Sie die einzelnen JavaScript-Kommandos nicht zu schnell hintereinander aus):
// Der erste Account muss über Ether verfügen (eventuell muss auf das Minig gewartet werden):
loadScript( 'src/checkAllBalances.js' )
// Weiteren Account anlegen (es werden mindestens zwei Accounts benötigt):
personal.newAccount( "Meine Ethereum-Test-Passphrase" )
web3.eth.accounts
// Smart Contract deployen:
personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" )
loadScript( 'src/MeinTokenDeploy.js' )
// Warten bis "Contract mined!" erscheint.
// Default-Account definieren und entsperren:
web3.eth.defaultAccount = web3.eth.accounts[0]
personal.unlockAccount( web3.eth.defaultAccount, "Meine Ethereum-Test-Passphrase" )
// Kontostände des neuen Tokens überprüfen (zu Beginn: 888 / 0, siehe: MeinTokenDeploy.js):
MeinToken.balanceOf( web3.eth.accounts[0] )
MeinToken.balanceOf( web3.eth.accounts[1] )
// Token-Transfer ausführen (188 Tokens transferieren):
MeinToken.transfer( web3.eth.accounts[1], 188 );
// Ca. 30 Sekunden auf das Mining warten.
// Geänderte Kontostände überprüfen (700 / 188):
MeinToken.balanceOf( web3.eth.accounts[0] )
MeinToken.balanceOf( web3.eth.accounts[1] )
Sie erhalten:
... > MeinToken.transfer( web3.eth.accounts[1], 188 ); "0xc99b4ebfc87e24c6e77462f8ddf360ab716da385d114b83304479514e19e0f41" > > MeinToken.balanceOf( web3.eth.accounts[0] ) 700 > MeinToken.balanceOf( web3.eth.accounts[1] ) 188
Sie haben erfolgreich einen Smart Contract mit eigenen Tokens implementiert und Tokens von einem Account zu einem anderen transferiert.
Führen Sie in der Geth-JavaScript-Console aus:
MeinToken.address
Diese 40-stellige Hexadresse müssen Sie sich für die weiter unten folgenden Demos merken. Speichern Sie die Adresse in die Datei MeinToken.address.priv.txt.
Dieser kleinen Mini-Token-Demo fehlt natürlich noch viel Funktionalität für einen richtigen Token. Sehen Sie sich hierzu das ERC20-Token-Beispiel an.
Wie die obige Mini-Token-Smart-Contract: MeinToken-Demo statt mit den Kommandozeilen-Tools Geth und Solc mit der grafischen Entwicklungsumgebung Remix Online Solidity Compiler realisiert werden kann.
Der Webbrowser-basierte Remix Online Solidity Compiler kann nicht nur Solidity-Code kompilieren, sondern bietet auch Syntax-Highlighting, Auto-Vervollständigung (Code Completion) und Find Usages. Und er kann Syntax-Fehler anzeigen, fertigen Web3-JavaScript-Code zum Deployen erstellen, und sogar Solidity-Code deployen, ausführen und debuggen.
Sehen Sie sich die Doku zur Remix - Solidity IDE
und das Remix-Wiki an.
Im Folgenden wird die Implementierung und Benutzung des obigen
Mini-Token-Demo-Smart-Contracts
demonstriert.
Ihre eigene lokale Test-Ethereum-Blockchain muss installiert sein, möglichst so ähnlich wie oben unter Basisinstallationen: Geth, Solc, private Test-Ethereum-Blockchain beschrieben.
Starten Sie die Test-Ethereum-Blockchain inklusive Mining:
cd \MeinWorkspace\EthereumDemo
starte-Test-Ethereum-Blockchain.bat
Öffnen Sie die Webseite vom Remix Online Solidity Compiler: http://remix.ethereum.org.
Starten Sie ein neues Projekt, indem Sie auf der Webseite oben links auf das kleine Pluszeichen klicken.
Geben Sie bei "File Name" ein: MeinToken.sol
In dem oberen mittleren leeren Fenster mit dem Titel "browser/MeinToken.sol" fügen Sie den Solidity-Skript-Sourcecode von MeinToken.sol ein.
Klicken Sie im rechten Drittel oben auf den Tabulatorreiter "Compile" und dann auf den Button "Start to compile".
Klicken Sie auf die Schaltfläche "Details", um die Kompilierergebnisse anzuzeigen.
Unter "WEB3DEPLOY" finden Sie ein fertiges JavaScript-Programm zum Deployen des MeinToken-Smart-Contracts.
Dieses JavaScript könnten Sie so wie oben gezeigt mit dem Kommandozeilen-Tool Geth
in der Geth-JavaScript-Console ausführen.
In dieser Demo soll jedoch stattdessen alles über die grafische Entwicklungsumgebung ausgeführt werden.
Verbinden Sie den Remix Online Solidity Compiler mit Ihrer eigenen lokalen Test-Ethereum-Blockchain:
Klicken Sie im rechten Drittel oben auf den Tabulatorreiter "Run",
wählen Sie bei Environment "Web3 Provider",
bestätigen Sie "Are you sure you want to connect to an ethereum node?" sowie
"Web3 Provider Endpoint" "http://localhost:8545".
Testen Sie die Verbindung:
Geben Sie im unteren mittleren Fenster ganz unten neben dem ">"-Zeichen ein:
web3.eth.accounts
Sie erhalten die Adressen Ihrer Accounts, beispielsweise:
> web3.eth.accounts [ "0x4597a26af9991b297b5ccc2a8c0966e9a1a17035", "0x63d7d5b64dc9cc0744dedf87971f8b0777d7e226" ]
Deployen Sie Ihren MeinToken-Smart-Contract in die lokale Blockchain:
Geben Sie im unteren mittleren Fenster ganz unten neben dem ">"-Zeichen ein:
web3.personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" )
Geben Sie anschließend im mittleren Teil des rechten Fensters bei "uint256 initialSupply"
einen anfänglichen Token-Betrag ein, beispielsweise 888,
und betätigen Sie daneben den "Create"-Button.
Sie erhalten beispielsweise:
> web3.personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" ) true creation of MeinToken pending... [block:2532 txIndex:0] from:0x459...17035, to:MeinToken.(constructor), value:0 wei, 0 logs, data:0x606...00378, hash:0xc25...c86b1
Der MeinToken-Smart-Contract ist erfolgreich in die lokale Blockchain deployt.
Fragen Sie zu Ihren ersten beiden mit web3.eth.accounts ermittelten Account-Adressen
den Token-Kontostand ab:
Geben Sie im rechten Drittel neben dem "balanceOf"-Button bei "address"
nacheinander die beiden Account-Adressen ein und betätigen Sie jeweils den "balanceOf"-Button.
Achten Sie dabei darauf, dass Sie die Hex-Adressen mit vorangestelltem "0x"
sowie eingeschlossen in Anführungszeichen eingeben, bespielsweise so:
"0x4597a26af9991b297b5ccc2a8c0966e9a1a17035"
Sie erhalten 888 und 0.
Führen Sie mit dem MeinToken-Smart-Contract einen Transfer von Tokens aus:
Geben Sie im unteren mittleren Fenster ganz unten neben dem ">"-Zeichen ein:
web3.personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" )
Geben Sie anschließend im rechten Drittel neben dem "transfer"-Button bei "address _to, uint256 _value"
die zweite Account-Adresse (wieder inklusive Anführungszeichen) und einen Betrag ein, beispielsweise so:
"0x63d7d5b64dc9cc0744dedf87971f8b0777d7e226", 111
Betätigen Sie den "transfer"-Button und warten Sie etwas auf das Mining.
Überprüfen Sie wieder über den "balanceOf"-Button die Token-Kontostände der beiden Accounts:
Sie erhalten 777 und 111.
Es wurden erfolgreich Tokens im MeinToken-Smart-Contract zwischen Accounts transferiert.
Wie statt des Kommandozeilen-Tools Geth das GUI-Tool Mist eingesetzt werden kann.
Wie Mist mit der eigenen Ethereum-Blockchain interagiert.
Wie mit Mist der im obigen Beispiel deployte Smart Contract "MeinToken.sol" verwendet werden kann, wie dessen Transfer-Methode aufgerufen wird, und wie die Kontostände des implementierten Tokens abgefragt werden.
Informationen zum Ethereum-Client Mist finden Sie beispielsweise unter: Ethereum Mist Wiki bei GitHub, Ethereum Mist bei GitHub, Using Mist Ethereum Wallet, Getting Started with Ethereum Mist Wallet.
Führen Sie die im Folgenden beschriebenen Schritte aus.
Falls Sie Mist noch nicht installiert haben:
Downloaden Sie von
Ethereum Wallet and Mist Cliente Ligero (GitHub)
die für Ihr Betriebssystem geeignete Installationsdatei, beispielsweise für Windows:
Mist-installer-0-9-3.exe.
Führen Sie die Installationsdatei aus.
Alle oben unter Mini-Token-Smart-Contract: MeinToken-Demo beschriebenen Schritte müssen ausgeführt worden sein, Ihre Ethereum-Blockchain muss aktiv sein, und der MeinToken-Smart-Contract muss deployt sein.
Falls noch nicht geschehen, starten Sie die Test-Ethereum-Blockchain:
cd \MeinWorkspace\EthereumDemo
starte-Test-Ethereum-Blockchain.bat
Führen Sie in der Geth-JavaScript-Console aus:
web3.eth.accounts
Sie erhalten die Hex-Adressen der beiden Accounts. Diese Adressen benötigen Sie weiter unten.
Starten Sie Mist. Stellen Sie sicher, dass sich Mist mit Ihrer eigenen Test-Ethereum-Blockchain verbunden hat.
Klicken Sie oben links auf "Wallets".
Rechts oben wird der standig steigende Kontostand in Ether angezeigt.
Weiter unten finden Sie unter "Konten Übersicht" die beiden von Ihnen eingerichteten Accounts.
Wenn Sie auf einen einzelnen Account klicken, wird die jeweilige 40-stellige Account-Adresse angezeigt.
Unter "Konto hinzufügen" könnten Sie weitere Accounts einrichten.
Klicken Sie oben rechts auf "Verträge".
Unter "Neue Verträge veröffentlichen" könnten Sie einen weiteren Smart Contract erstellen und deployen.
Um den bereits deployten Smart Contract zu verwenden,
klicken Sie unter "Eigene Verträge" auf "Verträge beobachten".
Es erscheint ein Eingabedialog. Geben Sie ein:
- Vertragsadresse: Die in der Datei
"MeinToken.address.priv.txt"
gespeicherte Adresse des Smart Contracts,
- Vertragsname: MeinToken,
- JSON-Schnittstelle: Der Inhalt der Datei "EthereumDemo\target\MeinToken.abi".
Unter "Eigene Verträge" wird Ihr Smart Contract "MeinToken" angezeigt.
Unter "Vertrag auslesen" / "Balance of" können Sie die Kontostände des implementierten Tokens abfragen: Geben Sie eine der beiden Account-Adressen ein: Sie erhalten den letzten Kontostand, also 700 bzw. 188 Tokens.
Unter "In Vertrag schreiben" wählen Sie bei "Select function" die Methode "Transfer".
Dabei erscheinen zwei Eingabefelder für die beiden Parameter der Transfer-Methode:
- to address: Hier tragen Sie die Account-Adresse des zweiten Accounts ein,
- value: Hier tragen Sie die Anzahl der zu übertragenen Tokens ein, z.B. 111.
Klicken Sie auf "Ausführen".
Es folgt ein Dialog, in dem nach dem Passwort gefragt wird: Geben Sie "Meine Ethereum-Test-Passphrase" ein,
und betätigen Sie "Send Transaction".
Erfragen Sie erneut unter "Vertrag auslesen" / "Balance of" die beiden Token-Kontostände:
Sie erhalten die geänderten Werte 589 und 299.
Die Smart-Contract-Transaktion wurde erfolgreich ausgeführt.
Wie eine DApp (Decentralized Application) erstellt werden kann, welche den obigen Ethereum Smart Contract MeinToken verwendet.
Wie hierzu eine Webseite mit Node.js erstellt wird.
Verwendet werden folgende Versionen:
git 2.16.1
Node.js 9.6.1
npm 5.6.0
web3 0.20.4
Windows 10
Sehen Sie sich die Doku zu git, npm, Node.js und web3.js an.
Falls Sie neuere als die oben genannten Versionen verwenden, erhalten Sie eventuell Fehlermeldungen wegen fehlender web3.min.js. Beachten Sie in diesem Fall die Webseite: web3.min.js is missing.
Führen Sie die im Folgenden beschriebenen Schritte aus.
Alle oben unter Mini-Token-Smart-Contract: MeinToken-Demo beschriebenen Schritte müssen ausgeführt worden sein.
Wechseln Sie in Ihr Workspace-Verzeichnis (z.B. \MeinWorkspace) und legen Sie parallel zum EthereumDemo-Projektverzeichnis ein neues Projektverzeichnis an:
cd \MeinWorkspace
mkdir EthNodejsDApp
cd EthNodejsDApp
Sie benötigen eine möglichst aktuelle Version von Git. Falls Sie kein Git installiert haben, oder falls Ihre Git-Version nicht aktuell genug ist, downloaden Sie von https://git-scm.com/download/ die für Ihr Betriebssystem geeignete Installationsdatei, beispielsweise für Windows Git-2.16.1-64-bit.exe, und führen Sie damit die Installation von Git durch. Kontrollieren Sie die Installation mit:
git --version
git --help
Sie benötigen eine aktuelle Version von Node.js. Downloaden Sie von https://nodejs.org die für Ihr Betriebssystem geeignete Installationsdatei, beispielsweise für Windows node-v9.6.1-x64.msi, und führen Sie damit die Installation von Node.js durch. Kontrollieren Sie die Installation mit:
node -v
node -h
npm -v
npm -h
Wechseln Sie in Ihr neues EthNodejsDApp-Projektverzeichnis und initiieren Sie ein Node.js-Projekt:
cd \MeinWorkspace\EthNodejsDApp
npm install web3@0.20.4
npm install ethereum/web3.js
npm init
npm list
type package.json
dir node_modules\web3\dist\web3.min.js
Erstellen Sie im EthNodejsDApp-Projektverzeichnis die DApp-HTML-Datei:
MeinTokenDAppWebseite.html
<html> <head> <title>MeinToken-Smart-Contract-DApp</title> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <style>* { font-family: sans-serif; }</style> <script type="text/javascript" src="./node_modules/web3/dist/web3.min.js"></script> <script type="text/javascript"> // Hier die Adresse des deployten MeinToken-Smart-Contracts eintragen: const contractAddress = "0xf07a9248ac043cf039a6f76113b78f89d483b2c8"; // Hier den Inhalt der Datei target\MeinToken.abi eintragen: const abi = '[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf",\ "outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},\ {"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],\ "name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},\ {"inputs":[{"name":"initialSupply","type":"uint256"}],"payable":false,"stateMutability":"nonpayable",\ "type":"constructor"}]'; const Web3 = require( 'web3' ); const web3 = new Web3( new Web3.providers.HttpProvider( 'http://localhost:8545' ) ); const meinTokenContract = web3.eth.contract( JSON.parse( abi ) ); const meinTokenContractInstance = meinTokenContract.at( contractAddress ); const account0 = web3.eth.accounts[0]; const account1 = web3.eth.accounts[1]; web3.eth.defaultAccount = web3.eth.accounts[0]; function meinTokenGetBalanceOf() { try { var balanceOf0 = meinTokenContractInstance.balanceOf( account0 ); var balanceOf1 = meinTokenContractInstance.balanceOf( account1 ); document.getElementById('account0').innerText = "Account 0 (" + account0 + "): balanceOf: " + balanceOf0; document.getElementById('account1').innerText = "Account 1 (" + account1 + "): balanceOf: " + balanceOf1; } catch( e ) { document.write( "<br/><b>Error: " + e.message + "</b>" ); } } function meinTokenTransfer() { var betrag = document.MeinFormular.betrag.value; var passphrase = document.MeinFormular.passphrase.value; if( null == betrag || "" == betrag ) { alert( "Betrag fehlt!" ); return false; } if( null == passphrase || "" == passphrase ) { alert( "Passphrase fehlt!" ); return false; } var message = "Transfer startet mit Betrag " + betrag + "."; document.getElementById('message').innerText = message; try { if( passphrase != "-" ) { web3.personal.unlockAccount( account0, passphrase ); } meinTokenContractInstance.transfer( account1, betrag ); if( passphrase != "-" ) { web3.personal.lockAccount( account0 ); } message += "\nCirca 30 Sekunden auf das Mining warten, und dann mit F5 refreshen." document.getElementById('message').innerText = message; alert( message ); } catch( e ) { document.write( "<br/><b>Error: " + e.message + "</b>" ); } } </script> </head> <body bgcolor="#EEFFEE" onload="meinTokenGetBalanceOf()"> <h1>DApp-Webseite zum MeinToken-Smart-Contract</h1> <div id="account0"></div> <div id="account1"></div> <br/> <form name="MeinFormular" method="post" onSubmit="meinTokenTransfer()"> Betrag: <input type="text" name="betrag" size="10" maxlength="10"> Passphrase: <input type="text" name="passphrase" size="45" maxlength="42"> <input type="submit" value="starte Transfer"> </form> <div id="message"></div> </body> </html>
Bei "const contractAddress = ..." müssen Sie die in der Datei "MeinToken.address.priv.txt" gespeicherte Adresse des MeinToken-Smart-Contracts eintragen.
Die Projektstruktur sieht jetzt so aus:
[\MeinWorkspace] |- [EthereumDemo] | '- ... '- [EthNodejsDApp] |- MeinTokenDAppWebseite.html |- package-lock.json |- package.json '- [node_modules] '- ...
Falls noch nicht geschehen, starten Sie die Test-Ethereum-Blockchain:
cd \MeinWorkspace\EthereumDemo
starte-Test-Ethereum-Blockchain.bat
Öffnen Sie die DApp-Webseite:
cd \MeinWorkspace\EthNodejsDApp
start MeinTokenDAppWebseite.html
Sie erhalten beispielsweise:
Merken Sie sich die jetzigen Kontostände, geben Sie einen Betrag an zu transferierenden Tokens und die Passphrase "Meine Ethereum-Test-Passphrase" ein, und betätigen Sie den "starte Transfer"-Button. Warten Sie ausreichend lange, damit die Transaktion per Mining der Blockchain hinzugefügt wird. Anschließend refreshen Sie die Webseite, in den meisten Webbrowsern mit F5 oder mit Strg+R. Sie sehen die um den eingegebenen Betrag geänderten Kontostände.
Wie Sie mit Java mit der Ethereum-Blockchain interagieren können.
Wie hierzu ein Maven-Projekt mit der Einbindung von Web3j aufgesetzt wird.
Wie mit dem Java-Programm Ether transferiert werden können.
Verwendet werden folgende Versionen:
Java JDK 9
Maven 3.5.0
geth 1.8.2
Web3j 3.3.1
Windows 10
Sehen Sie sich an: Java, JDK 9, Maven, Web3j-Projekt, Web3j-Doku, Web3j-Doku.
Führen Sie die im Folgenden beschriebenen Schritte aus.
Ein aktuelles JDK 9 und ein aktuelles Maven müssen installiert sein.
Eine Java-IDE wie beispielsweise JetBrains IntelliJ IDEA oder Eclipse ist nicht zwingend notwendig, aber vereinfacht den Entwicklungsprozess enorm.
Wechseln Sie in Ihr Workspace-Verzeichnis (z.B. \MeinWorkspace) und führen Sie folgende Kommandos aus:
cd \MeinWorkspace
mkdir EthJavaDApp
cd EthJavaDApp
md src\main\java\de\meinefirma\meinprojekt
tree /F
Erstellen Sie im EthJavaDApp-Projektverzeichnis die
Maven-Konfigurationsddatei:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>de.meinefirma.meinprojekt</groupId> <artifactId>EthJavaDApp</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>EthJavaDApp</name> <properties> <project.build.sourceEncoding>ISO-8859-1</project.build.sourceEncoding> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.web3j</groupId> <artifactId>core</artifactId> <version>3.3.1</version> </dependency> </dependencies> </project>
Erstellen Sie im src\main\java\de\meinefirma\meinprojekt-Unterverzeichnis die Java-Klasse:
EtherTransfer.java
package de.meinefirma.meinprojekt; import org.web3j.protocol.Web3j; import org.web3j.protocol.admin.Admin; import org.web3j.protocol.admin.methods.response.PersonalUnlockAccount; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.request.Transaction; import org.web3j.protocol.core.methods.response.EthGetTransactionCount; import org.web3j.protocol.core.methods.response.EthSendTransaction; import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.protocol.http.HttpService; import org.web3j.tx.Contract; import org.web3j.tx.ManagedTransaction; import org.web3j.utils.Convert; import java.math.BigInteger; import java.util.Optional; public class EtherTransfer { public static void main( String[] args ) throws Exception { // Programm so erweitern, dass Parameter ausgelesen oder uebergeben werden: transferiereEther( "http://localhost:8545", // Hier die Adressen der beiden Accounts eintragen (Ergebnis von: web3.eth.accounts): "0x4597a26af9991b297b5ccc2a8c0966e9a1a17035", "0x63d7d5b64dc9cc0744dedf87971f8b0777d7e226", // Hier den zu transferierenden Betrag und die Passphrase eintragen: "10", "Meine Ethereum-Test-Passphrase" ); } public static void transferiereEther( String ethereumUrl, String senderAdresse, String empfaengerAdresse, String betragEther, String passphrase ) throws Exception { Admin admin = Admin.build( new HttpService( ethereumUrl ) ); PersonalUnlockAccount personalUnlockAccount = admin.personalUnlockAccount( senderAdresse, passphrase ).send(); if( !personalUnlockAccount.accountUnlocked() ) { System.out.println( "\nFehler: Account-Unlock fehlgeschlagen." ); return; } Web3j web3j = Web3j.build( new HttpService( ethereumUrl ) ); EthGetTransactionCount txCount = web3j.ethGetTransactionCount( senderAdresse, DefaultBlockParameterName.LATEST ).sendAsync().get(); BigInteger nonce = txCount.getTransactionCount(); BigInteger betrag = Convert.toWei( betragEther, Convert.Unit.ETHER ).toBigInteger(); Transaction transaction = Transaction.createEtherTransaction( senderAdresse, nonce, ManagedTransaction.GAS_PRICE, Contract.GAS_LIMIT, empfaengerAdresse, betrag ); EthSendTransaction response = web3j.ethSendTransaction( transaction ).sendAsync().get(); if( response.hasError() ) { System.out.println( "\nFehler: " + response.getError().getMessage() ); return; } String txHash = response.getTransactionHash(); System.out.print( "\nTxHash: " + txHash + "\nWarten auf das Mining " ); for( int i = 0; i < 600; i++ ) { Optional<TransactionReceipt> transactionReceipt = web3j.ethGetTransactionReceipt( txHash ).send().getTransactionReceipt(); if( transactionReceipt.isPresent() ) { System.out.println( "\nTransfer von " + betragEther + " Ether abgeschlossen, benutzte Gas-Menge: " + transactionReceipt.get().getGasUsed() ); return; } System.out.print( "." ); Thread.sleep( 1000 ); } System.out.println( "\nDer Transfer konnte innerhalb von 10 Minuten nicht abgeschlossen werden. Ist Mining aktiv?" ); } }
Ersetzen Sie in der main()-Methode die Adressen der beiden Accounts durch Ihre.
Kompilieren Sie EtherTransfer.java:
cd \MeinWorkspace\EthJavaDApp
mvn clean package
Das Java-Programm ist kompilierbar, und Web3j wurde erfolgreich eingebunden.
Die Projektstruktur sieht jetzt so aus:
[\MeinWorkspace\EthJavaDApp] |- pom.xml |- [src] | '- [main] | '- [java] | '- [de] | '- [meinefirma] | '- [meinprojekt] | '- EtherTransfer.java '- [target] |- EthJavaDApp-1.0-SNAPSHOT.jar '- ...
Alle oben unter Basisinstallationen: Geth, Solc, private Test-Ethereum-Blockchain beschriebenen Schritte müssen ausgeführt worden sein.
Starten Sie die Test-Ethereum-Blockchain:
cd \MeinWorkspace\EthereumDemo
starte-Test-Ethereum-Blockchain.bat
Fragen Sie in der Geth-JavaScript-Console die Accounts und Ether-Kontostände ab:
loadScript( 'src/checkAllBalances.js' )
Es müssen mindestens zwei Accounts existieren. Falls es nur einen gibt, legen Sie einen weiteren an, wie oben beschrieben.
Jetzt gibt es drei Möglichkeiten:
Die sinnvollste Variante ist, zumindest während der Entwicklungszeit eine IDE (z.B. IntelliJ IDEA oder Eclipse) zu verwenden, in der Sie das Projekt öffnen und ausführen.
Alternativ können Sie die pom.xml um das Assembly-Plugin erweitern, wie beschrieben unter:
Ausführbare Jar-Datei inklusive Abhängigkeiten mit dem Assembly Plugin.
Dann können Sie die erzeugte Jar-Datei direkt ausführen.
Eine dritte Möglichkeit wäre, alle benötigten Jar-Libs in ein Unterverzeichnis zu kopieren, dieses Verzeichnis dem Classpath hinzuzufügen, und damit die kompilierte EtherTransfer.class auszuführen.
Führen Sie das EtherTransfer-Programm aus.
Sie erhalten beispielsweise:
TxHash: 0xdc96314d6443c81d86c7a4afc6413b2b4127fee739b9bb7ae607ef7267bad417 Warten auf das Mining ........................................ Transfer von 10 Ether abgeschlossen, benutzte Gas-Menge: 21000
Überprüfen Sie mit Ihrer Wallet-Anwendung oder in der Geth-JavaScript-Console mit loadScript( 'src/checkAllBalances.js' ) die Ether-Kontostände.
Es wurden erfolgreich 10 Ether von einem Account zu einem anderen transferiert.
Wie Sie mit Java mit einem Smart Contract in der Ethereum-Blockchain interagieren können.
Wie dazu automatisiert aus den Smart-Contract-Definitionsdateien eine Java-Schnittstellenklasse generiert wird.
Wie mit einem Java-Programm eigene Smart-Contract-Tokens transferiert werden können.
In diesem Beispiel wird eine Java-Kommandozeilenanwendung erstellt. Weiter unten unter DApp-Webseite für den Smart Contract mit Java wird eine entsprechende DApp-Webseite erstellt.
Als Smart Contract wird der oben gezeigte MeinToken-Smart-Contract verwendet.
Führen Sie die im Folgenden beschriebenen Schritte aus.
Sowohl alle oben unter MeinToken-Smart-Contract als auch alle unter Transfer von Ether mit Web3j und Java beschriebenen Schritte müssen ausgeführt worden sein.
Um die Java-Schnittstellenklasse zur Interaktion mit dem Smart Contract zu erstellen, wird das Web3j-Kommandozeilen-Tool benötigt. Downloaden Sie web3j-3.3.1.zip von https://github.com/web3j/web3j/releases. Entzippen Sie diese Datei in das Unterverzeichnis web3j-3.3.1 in Ihrem EthJavaDApp-Projektverzeichnis. Überprüfen Sie die Installation mit:
cd \MeinWorkspace\EthJavaDApp
web3j-3.3.1\bin\web3j.bat version
web3j-3.3.1\bin\web3j.bat solidity
Die folgenden Kommandos gehen davon aus, dass der MeinToken-Smart-Contract im Projektverzeichnis \MeinWorkspace\EthereumDemo parallel zum aktuellen Projektverzeichnis \MeinWorkspace\EthJavaDApp angelegt wurde. Falls das bei Ihnen anders ist, müssen Sie die Pfade anpassen. Führen Sie aus:
cd \MeinWorkspace\EthJavaDApp
web3j-3.3.1\bin\web3j.bat solidity generate ..\EthereumDemo\target\MeinToken.bin ..\EthereumDemo\target\MeinToken.abi -o src\main\java -p de.meinefirma.meinprojekt
dir src\main\java\de\meinefirma\meinprojekt
Es wurde die neue Java-Klasse MeinToken.java generiert.
Erstellen Sie im src\main\java\de\meinefirma\meinprojekt-Unterverzeichnis die Java-Klasse:
MeinTokenTransfer.java
package de.meinefirma.meinprojekt; import org.web3j.crypto.CipherException; import org.web3j.crypto.Credentials; import org.web3j.crypto.WalletUtils; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.RemoteCall; import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.protocol.http.HttpService; import org.web3j.tx.Contract; import org.web3j.tx.ManagedTransaction; import java.io.*; import java.math.BigInteger; import java.util.*; import java.util.concurrent.CompletableFuture; public class MeinTokenTransfer { // Hier die Adresse des MeinToken-Smart-Contracts in der Blockchain // und entweder die Passphrase oder den Private Key eintragen: public static final String DEFAULT_CONTRACT_ADRESSE = "0xf07a9248ac043cf039a6f76113b78f89d483b2c8"; public static final String DEFAULT_PASSPHRASE_OR_PRIVKEY = "Meine Ethereum-Test-Passphrase"; public static final String DEFAULT_ETHEREUM_URL = "http://localhost:8545"; public static final List<String> DEFAULT_KEYSTORE_DIRS = Arrays.asList( "../EthereumDemo/Private-Blockchain/keystore", "../EthereumDemo/Rinkeby-Blockchain/keystore" ); public static void main( String[] args ) throws Exception { if( args.length < 1 ) { System.out.println( "\nFolgende Parameter koennen uebergeben werden:\n" + " Anzahl zu transferierender Tokens\n" + " Passphrase oder Private Key\n" + " Sender-Account-Adresse\n" + " Empfaenger-Account-Adresse\n" + " MeinToken-Contract-Adresse\n" + " Keystore-Verzeichnis\n" + " Ethereum-Blockchain-URL\n" ); } String betragStr = ( args.length > 0 ) ? args[0] : "0"; String pssPhrOrPrvK = ( args.length > 1 ) ? args[1] : DEFAULT_PASSPHRASE_OR_PRIVKEY; String contractAdr = ( args.length > 4 ) ? args[4] : DEFAULT_CONTRACT_ADRESSE; String keystoreDir = ( args.length > 5 ) ? args[5] : ""; String ethereumUrl = ( args.length > 6 ) ? args[6] : DEFAULT_ETHEREUM_URL; List<String> accounts = ermittleAccounts( ethereumUrl ); String senderAdresse = ( args.length > 2 ) ? args[2] : accounts.get( 0 ); String empfaengerAdresse = ( args.length > 3 ) ? args[3] : accounts.get( 1 ); System.out.println( "Accounts: " + accounts ); List<String> keystoreDirs = new ArrayList<>(); keystoreDirs.add( keystoreDir ); keystoreDirs.addAll( DEFAULT_KEYSTORE_DIRS ); MeinToken meinToken = null; List<BigInteger> kontostaende = null; try { meinToken = initMeinToken( ethereumUrl, contractAdr, senderAdresse, pssPhrOrPrvK, keystoreDirs ); kontostaende = liesKontostaende( meinToken, senderAdresse, empfaengerAdresse ); System.out.println( "Kontostaende: " + kontostaende.get( 0 ) + " / " + kontostaende.get( 1 ) ); } catch( Exception ex ) { System.out.println( "Fehler bei der Abfrage des MeinToken Smart Contracts. " + "Sind Contract-Adresse sowie Passphrase bzw. Private Key korrekt?" ); ex.printStackTrace(); return; } long betrag = Long.parseLong( betragStr ); if( betrag > 0 ) { System.out.println( betrag + " Tokens werden transferiert, ca. 30 Sekunden auf das Mining warten ..." ); kontostaende = transferiereTokens( meinToken, senderAdresse, empfaengerAdresse, betrag, 180 ); System.out.println( "Kontostaende: " + kontostaende.get( 0 ) + " / " + kontostaende.get( 1 ) ); } } public static List<String> ermittleAccounts( String ethereumUrl ) throws IOException { Web3j web3j = Web3j.build( new HttpService( ethereumUrl ) ); return web3j.ethAccounts().send().getAccounts(); } public static MeinToken initMeinToken( String ethereumUrl, String meinTokenContractAdresse, String account, String passphraseOrPrivateKey, List<String> keystoreDirs ) { Web3j web3j = Web3j.build( new HttpService( ethereumUrl ) ); Credentials credentials = null; try { credentials = getCredentialsFromKeystore( account, passphraseOrPrivateKey, keystoreDirs ); } catch( Exception ex1 ) { try { credentials = Credentials.create( passphraseOrPrivateKey ); } catch( Exception ex2 ) { throw new RuntimeException( ex1 ); } } return MeinToken.load( meinTokenContractAdresse, web3j, credentials, ManagedTransaction.GAS_PRICE, Contract.GAS_LIMIT ); } public static List<BigInteger> liesKontostaende( MeinToken meinToken, String senderAdresse, String empfaengerAdresse ) throws Exception { List<BigInteger> kontostaende = new ArrayList<>(); kontostaende.add( meinToken.balanceOf( senderAdresse ).send() ); kontostaende.add( meinToken.balanceOf( empfaengerAdresse ).send() ); return kontostaende; } public static List<BigInteger> transferiereTokens( MeinToken meinToken, String senderAdresse, String empfaengerAdresse, long betrag, int timeoutSek ) throws Exception { if( betrag > 0 ) { RemoteCall<TransactionReceipt> rc = meinToken.transfer( empfaengerAdresse, BigInteger.valueOf( betrag ) ); CompletableFuture<TransactionReceipt> txFuture = rc.sendAsync(); for( int i = 0; i < timeoutSek; i++ ) { if( txFuture.isDone() ) { break; } System.out.print( "." ); Thread.sleep( 1000 ); } if( timeoutSek > 0 ) { System.out.println( "" ); } if( txFuture.isCompletedExceptionally() || txFuture.isCancelled() ) { throw new Exception( "MeinToken-Transfer fehlgeschlagen: " + txFuture.get().getStatus() ); } if( !txFuture.isDone() && timeoutSek > 0 ) { throw new Exception( "Der MeinToken-Transfer konnte nicht innerhalb von " + timeoutSek + " Sekunden abgeschlossen werden. Ist Mining aktiv?" ); } if( txFuture.isDone() ) { TransactionReceipt txReceipt = txFuture.get(); System.out.println( "MeinToken-Transfer erfolgreich, GasUsed: " + txReceipt.getGasUsed() + ", BlockNumber: " + txReceipt.getBlockNumber() + ", TxHash: " + txReceipt.getTransactionHash() ); } } return liesKontostaende( meinToken, senderAdresse, empfaengerAdresse ); } private static Credentials getCredentialsFromKeystore( String account, String passphrase, List<String> keystoreDirs ) throws IOException, CipherException { File[] ksDateien = null; for( String ksDir : keystoreDirs ) { ksDateien = new File( ksDir ).listFiles( f -> f.isFile() && f.getName().contains( account.substring( 2 ) ) ); if( ksDateien != null && ksDateien.length != 0 ) { break; } } if( ksDateien == null || ksDateien.length == 0 ) { throw new IOException( "Fehler: Keine zum Account " + account + " passende Keystore-Datei in den Keystore-Verzeichnissen " + keystoreDirs + " gefunden." ); } return WalletUtils.loadCredentials( passphrase, ksDateien[0] ); } }
Tragen Sie bei DEFAULT_PASSPHRASE_OR_PRIVKEY und keystoreDirs die Werte aus Ihrer Blockchain ein, und bei DEFAULT_CONTRACT_ADRESSE die in MeinToken.address.priv.txt gespeicherte Smart-Contract-Adresse.
Die beiden Projektstrukturen sehen jetzt so aus:
[\MeinWorkspace\EthereumDemo] |- build-MeinToken.bat |- MeinToken.address.priv.txt |- priv-geth.log |- starte-Test-Ethereum-Blockchain.bat |- [Private-Blockchain] | |- history | |- [geth] | | '- ... | '- [keystore] | |- UTC--2018-01-26T18-36-00... | '- UTC--2018-01-26T18-37-34... |- [solc] | '- ... |- [src] | '- ... '- [target] |- MeinToken.abi '- MeinToken.bin
[\MeinWorkspace\EthJavaDApp] |- pom.xml |- [src] | '- [main] | '- [java] | '- [de] | '- [meinefirma] | '- [meinprojekt] | |- EtherTransfer.java | |- MeinToken.java | '- MeinTokenTransfer.java |- [target] | '- ... '- [web3j-3.3.1] |- [bin] | '- web3j | '- web3j.bat '- [lib] '- ...
Kompilieren Sie die Java-Klassen:
cd \MeinWorkspace\EthJavaDApp
mvn clean package
Um die MeinTokenTransfer-Java-Klasse auszuführen, gibt es wieder die drei oben genannten Möglichkeiten.
Wenn Sie MeinTokenTransfer ohne Kommandozeilenparameter ausführen, erscheint ein Text, der die möglichen Parameter nennt, sowie die beiden Account-Adressen.
Wenn Sie MeinTokenTransfer aufrufen mit:
0 "Meine Ethereum-Test-Passphrase"
dann erhalten Sie die beiden Account-Adressen sowie deren MeinToken-Kontostände.
Wenn Sie MeinTokenTransfer aufrufen mit:
10 "Meine Ethereum-Test-Passphrase"
dann erhalten Sie beispielsweise:
Accounts: [0x4597a26af9991b297b5ccc2a8c0966e9a1a17035, 0x63d7d5b64dc9cc0744dedf87971f8b0777d7e226] Kontostaende: 828 / 60 10 Tokens werden transferiert, ca. 30 Sekunden auf das Mining warten ... ............................... MeinToken-Transfer erfolgreich, GasUsed: 33924, BlockNumber: 3390, TxHash: 0xae0d815da94ef13354dfc8... Kontostaende: 818 / 70
Warten Sie eine Weile auf das Mining und fragen Sie die Kontostände erneut ab.
Es wurden erfolgreich 10 MeinToken von einem Account zu einem anderen transferiert.
Falls Sie folgende Fehlermeldung erhalten:
org.web3j.protocol.exceptions.TransactionException: Error processing request: unknown transaction
Dann verwenden Sie wahrscheinlich eine ungünstige Kombination von Geth und Web3j. Beispielsweise bei Kombinationen von Geth 1.8.0 bis 1.8.1 mit Web3j 3.2.0 bis 3.3.1 gibt es diesen Fehler. Siehe hierzu: Web3j Issue 318.
Verwenden Sie Geth entweder in Version 1.7.3 oder in mindestens Version 1.8.2 um den Fehler zu vermeiden.
Wie eine DApp (Decentralized Application) erstellt werden kann, welche den obigen Ethereum Smart Contract MeinToken verwendet.
Wie ein Maven-Web-Projekt mit der Einbindung von Web3j und Verwendung des Jetty-Webservers aufgesetzt werden kann.
Wie mit der Java-DApp-Webseite eigene Smart-Contract-Tokens transferiert werden können.
Diese Demo verwendet JSP (JavaServer Pages) um das Beispiel kurz zu halten. In ernsthaften Anwendungen werden eher andere mit Java einsetzbare Web-Frameworks verwendet, wie etwa JSF, Spring MVC oder Angular.
Als Smart Contract wird wieder der oben gezeigte MeinToken-Smart-Contract verwendet.
Führen Sie die im Folgenden beschriebenen Schritte aus.
Die unter Transfer von eigenen Smart-Contract-Tokens mit Web3j und Java beschriebenen Schritte müssen ausgeführt worden sein.
Ersetzen Sie im EthJavaDApp-Projektverzeichnis den Inhalt der Maven-Projektkonfigurationsdatei:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>de.meinefirma.meinprojekt</groupId> <artifactId>EthJavaDApp</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>EthJavaDApp</name> <properties> <project.build.sourceEncoding>ISO-8859-1</project.build.sourceEncoding> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.8.v20171121</version> <configuration> <webAppConfig> <contextPath>/${project.artifactId}</contextPath> </webAppConfig> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.web3j</groupId> <artifactId>core</artifactId> <version>3.3.1</version> </dependency> </dependencies> </project>
Erzeugen Sie das zusätzliche Webapp-Verzeichnis:
cd \MeinWorkspace\EthJavaDApp
mkdir src\main\webapp\WEB-INF
Erstellen Sie im src\main\webapp\WEB-INF-Verzeichnis die Webapp-Konfigurationsdatei:
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>MeinToken-Smart-Contract-DApp (EthJavaDApp)</display-name> </web-app>
Erstellen Sie im src\main\webapp-Verzeichnis die JSP-Datei:
index.jsp
<%@ page import="java.math.BigInteger" %> <%@ page import="java.util.*" %> <%@ page import="de.meinefirma.meinprojekt.*" %> <% String ethereumUrl = MeinTokenTransfer.DEFAULT_ETHEREUM_URL; long newTimestamp = (new Date()).getTime(); List<String> accounts = MeinTokenTransfer.ermittleAccounts( ethereumUrl ); String contract = request.getParameter( "contract" ); String account0 = request.getParameter( "account0" ); String account1 = request.getParameter( "account1" ); String password = request.getParameter( "password" ); String betrag = request.getParameter( "betrag" ); String timestamp = request.getParameter( "timestamp" ); if( contract == null || contract.isEmpty() ) { contract = MeinTokenTransfer.DEFAULT_CONTRACT_ADRESSE; } if( account0 == null || account0.isEmpty() ) { account0 = accounts.get( 0 ); } if( account1 == null || account1.isEmpty() ) { account1 = accounts.get( 1 ); } if( password == null || password.isEmpty() ) { password = MeinTokenTransfer.DEFAULT_PASSPHRASE_OR_PRIVKEY; } if( betrag == null || betrag.isEmpty() ) { betrag = ""; } MeinToken meinToken = null; List<BigInteger> kontostaende = null; try { meinToken = MeinTokenTransfer.initMeinToken( ethereumUrl, contract, account0, password, MeinTokenTransfer.DEFAULT_KEYSTORE_DIRS ); kontostaende = MeinTokenTransfer.liesKontostaende( meinToken, account0, account1 ); } catch( Exception ex ) { out.println( "\n<br><font color='red'>Fehler bei der Abfrage des MeinToken Smart Contracts. " + "Sind Contract-Adresse sowie Passphrase bzw. Private Key korrekt?<br>\n" + ex.getMessage() + "</font><br><br>\n" ); kontostaende = new ArrayList<BigInteger>(); kontostaende.add( BigInteger.valueOf( -1 ) ); kontostaende.add( BigInteger.valueOf( -1 ) ); } if( meinToken != null && !betrag.isEmpty() && ("" + timestamp).equals( session.getAttribute( "timestamp" ) ) ) { try { long betr = Long.parseLong( betrag ); if( betr > 0 ) { System.out.println( "transferiere " + betr + " Tokens von " + account0 + " nach " + account1 ); kontostaende = MeinTokenTransfer.transferiereTokens( meinToken, account0, account1, betr, 180 ); } } catch( Exception ex ) { out.println( "\n<br><font color='red'>Fehler beim MeinToken-Transfer. " + "Ist die Passphrase bzw. der Private Key korrekt?<br>\n" + ex.getMessage() + "</font><br><br>\n" ); } } session.setAttribute( "timestamp", "" + newTimestamp ); %> <html> <head> <title>MeinToken-Smart-Contract-DApp</title> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <style>* { font-family: sans-serif; }</style> </head> <body bgcolor="#EEEEFF"> <h2>DApp-Webseite zum MeinToken-Smart-Contract mit Java</h2> <form name="meinFormular" method="post" onSubmit="alert( 'OK betaetigen, circa 30 Sekunden auf das Mining warten und dann mit F5 refreshen.' )"> <table border=0 cellspacing=9 cellpadding=0> <tr> <td valign="top">Contract:</td> <td><input type="text" name="contract" value='<%= contract %>' size='45' maxlength='42'><br></td> </tr> <tr> <td valign="top">Account0:</td> <td><input type="text" name="account0" value='<%= account0 %>' size='45' maxlength='42'>: <input type="text" value='<%= kontostaende.get( 0 ) %>' size='12' maxlength='10' readonly> MeinToken<br> <input type="text" name="password" value='<%= password %>' size='45' maxlength='66'><br></td> </tr> <tr> <td valign="top">Account1:</td> <td><input type="text" name="account1" value='<%= account1 %>' size='45' maxlength='42'>: <input type="text" value='<%= kontostaende.get( 1 ) %>' size='12' maxlength='10' readonly> MeinToken<br></td> </tr> <tr> <td valign="top">Betrag:</td> <td><input type="text" name="betrag" value='<%= betrag %>' size='12' maxlength='10'> <input type="submit" name="berechne" value="starte Transfer"><br> <input type="hidden" name="timestamp" value='<%= "" + newTimestamp %>'></td> </tr> </table> </form> </body> </html>
Überprüfen Sie die oben in MeinTokenTransfer.java eingetragenen Werte für DEFAULT_CONTRACT_ADRESSE, DEFAULT_PASSPHRASE_OR_PRIVKEY und DEFAULT_KEYSTORE_DIRS.
Die Projektstruktur sieht jetzt so aus:
[\MeinWorkspace\EthJavaDApp] |- pom.xml |- [src] | '- [main] | |- [java] | | '- [de] | | '- [meinefirma] | | '- [meinprojekt] | | |- EtherTransfer.java | | |- MeinToken.java | | '- MeinTokenTransfer.java | '- [webapp] | '- index.jsp | '- [WEB-INF] | '- web.xml |- [target] | '- ... '- [web3j-3.3.1] |- [bin] | '- web3j | '- web3j.bat '- [lib] '- ...
Falls noch nicht geschehen, starten Sie die Test-Ethereum-Blockchain:
cd \MeinWorkspace\EthereumDemo
starte-Test-Ethereum-Blockchain.bat
Starten Sie den Jetty-Webserver mit der EthJavaDApp-Webanwendung:
cd \MeinWorkspace\EthJavaDApp
mvn jetty:run-war
Warten Sie etwas, bis "Started Jetty Server" erscheint. Rufen Sie die Webseite auf:
Merken Sie sich die jetzigen Kontostände, geben Sie die gewünschten Account-Adressen, die Passphrase bzw. den Private Key und den Betrag an zu transferierenden Tokens ein, und betätigen Sie den "starte Transfer"-Button. Warten Sie ausreichend lange, damit die Transaktion per Mining der Blockchain hinzugefügt wird. Anschließend refreshen Sie die Webseite, in den meisten Webbrowsern mit F5 oder mit Strg+R. Sie sehen die um den eingegebenen Betrag geänderten Kontostände.
Wenn Sie die beiden Account-Adressen vertauschen, können Sie auch einen Transfer in die andere Richtung ausführen. Voraussetzung hierfür ist, dass der Account, von dem die Tokens transferiert werden, über ausreichend viele Ether verfügt, um die Transaktionsgebühren zu bezahlen, etwa indem Sie vorher mit Transfer von Ether mit Web3j und Java ein paar Ether transferieren.
Sie erhalten beispielsweise:
Wie Sie auf Ihrem PC eine synchronisierte Kopie der öffentlichen Rinkeby-Test-Ethereum-Blockchain erstellen.
Wie Sie in der öffentlichen Rinkeby-Test-Ethereum-Blockchain einen neuen Account anlegen.
Wie Sie zu diesem Account Ether transferieren.
Wie Sie über den Etherscan-Internetdienst den Kontostand abfragen können, und so zeigen, dass Ihr neu erstellter Account auch auf fremden Ethereum-Nodes existiert und öffentlich erreichbar ist.
Die bisherigen Demos verwendeten eine private nur auf dem eigenen PC existierende Ethereum-Blockchain. In diesem Beispiel wird die öffentliche Rinkeby-Test-Ethereum-Blockchain verwendet.
Führen Sie die im Folgenden beschriebenen Schritte aus.
Falls Sie bei den vorherigen Demos bereits das EthereumDemo-Projektverzeichnis angelegt haben, wechseln Sie in dieses Verzeichnis. Andernfalls legen Sie es an:
cd \MeinWorkspace
mkdir EthereumDemo
cd EthereumDemo
mkdir src
tree /F
Start-Skript:
Erstellen Sie im EthereumDemo-Projektverzeichnis die Batchdatei
starte-Rinkeby-Ethereum-Blockchain.bat
mit folgendem Inhalt (in einer Zeile):
geth --rinkeby --networkid 4 --datadir "Rinkeby-Blockchain" --rpc --rpcapi "db,eth,net,web3,personal,txpool" --rpccorsdomain "*" console 2>>rinkeby-geth.log
Start der Rinkeby-Test-Ethereum-Blockchain:
cd \MeinWorkspace\EthereumDemo
starte-Rinkeby-Ethereum-Blockchain.bat
Sie erhalten die Geth-JavaScript-Console, in der Sie Geth-JavaScript-Kommandos eingeben können.
Stellen Sie zuerst in einem zweiten Kommandozeilenfenster sicher, dass Sie die gewünschte Test-Ethereum-Blockchain betreiben (und nicht die echte Ethereum-Blockchain):
cd \MeinWorkspace\EthereumDemo
type rinkeby-geth.log
Ungefähr in der sechsten Zeile erscheint eine Meldung ähnlich zu:
INFO [02-02|16:17:18] Initialising Ethereum protocol versions="[63 62]" network=4
Wichtig ist dabei die network-Nummer: Für Rinkeby muss sie 4 lauten.
Sie können auch in der Geth-JavaScript-Console die network-Nummer überprüfen:
admin.nodeInfo
Beim erstmaligen Start der lokalen Rinkeby-Blockchain kann es stundenlang dauern, bis die komplette Blockchain auf den lokalen PC synchronisiert ist. Sie können dies in der Logdatei beobachten.
Skript zur Abfrage der Kontostände:
Falls Sie noch nicht wie
oben
vorgeschlagen im src-Verzeichnis die Datei
checkAllBalances.js angelegt haben, legen Sie sie an.
Account anlegen:
Wechseln Sie wieder in das Fenster mit der Geth-JavaScript-Console.
Legen Sie einen Account an (denken Sie sich ein schwierigeres Passwort aus und merken Sie es sich gut):
web3.eth.accounts
personal.newAccount( "Meine Ethereum-Test-Passphrase" )
web3.eth.accounts
Kontostände abfragen:
Jetzt können Sie Kontostände abfragen. Führen Sie in der Geth-JavaScript-Console aus:
loadScript( 'src/checkAllBalances.js' )
Sie erhalten:
0x39bbbe004272e72dba0c13a0e60443cfcceead3c: balance: 0 ether
Überprüfung der Keystore-Datei und der Verzeichnisstruktur:
Führen Sie aus:
dir \MeinWorkspace\EthereumDemo\Rinkeby-Blockchain\keystore
Dort befindet sich die Keystore-Datei, beispielsweise:
UTC--2018-02-02T16-17-18.123456789Z--39bbbe004272e72dba0c13a0e60443cfcceead3c
Die Projektstruktur sieht jetzt so aus (je nachdem welche Beispiele Sie ausgeführt haben, können es mehr oder weniger Dateien sein):
[\MeinWorkspace\EthereumDemo] |- build-MeinToken.bat |- MeinToken.address.priv.txt |- priv-geth.log |- rinkeby-geth.log |- starte-Rinkeby-Ethereum-Blockchain.bat |- starte-Test-Ethereum-Blockchain.bat |- [Private-Blockchain] | '- ... |- [Rinkeby-Blockchain] | |- [geth] | | |- [chaindata] | | | '- ... | | '- ... | '- [keystore] | '- UTC--2018-02-02T16-17-18... |- [solc] | '- ... |- [src] | |- checkAllBalances.js | |- genesis-block.json | |- MeinToken.sol | |- MeinTokenDeploy.js | |- meinTokenJsTeil1.txt | |- meinTokenJsTeil2.txt | '- meinTokenJsTeil3.txt '- [target] |- MeinToken.abi '- MeinToken.bin
Das Rinkeby-Blockchain\geth\chaindata-Verzeichnis enthält die Rinkeby-Blockchain-Daten und ist nach vollständiger Synchronisation mehrere Gigabyte groß.
Ether zum Account transferieren:
Da es sich um eine Test-Blockchain handelt, sind die Ether in dieser Blockchain wertlos.
Trotzdem benötigen Sie Ether auf Ihrem Test-Account, wenn Sie weitere Versuche ausführen wollen.
Unter
https://faucet.rinkeby.io
finden Sie hierzu eine Anleitung:
Sie benötigen eine "Social network URL" von Facebook, Google Plus oder Twitter.
Beispielsweise bei Google Plus geben Sie im Fenster "Was gibt's Neues bei dir?"
Ihre Account-Adresse (mit vorangestelltem "0x") ein (z.B.: 0x39bbbe004272e72dba0c13a0e60443cfcceead3c)
und klicken auf "[öffentlich] Posten".
Anschließend öffnen Sie den Post in einem eigenen Fenster und kopieren die dann im Webbrowser angezeigte URL,
beispielsweise: "https://plus.google.com/1016.../posts/ZZLx...".
Diese URL geben Sie bei
https://faucet.rinkeby.io
ein und wählen rechts daneben bei "Give me Ether": "3 Ethers / 8 hours".
Sie erhalten die Bestätigung "Funding request accepted".
Anschließend warten Sie ca. 1 Minute auf das Mining.
Da die Rinkeby-Ethereum-Blockchain eine öffentliche Blockchain ist, können Sie den Kontostand mit dem öffentlichen Etherscan-Internetdienst abfragen per: https://rinkeby.etherscan.io/address/{Meine-Account-Adresse} (ersetzen Sie "{Meine-Account-Adresse}" durch Ihre Account-Adresse).
Ermitteln Sie die Kontostände auch erneut über die Geth-JavaScript-Console:
loadScript( 'src/checkAllBalances.js' )
Sie erhalten:
0x39bbbe004272e72dba0c13a0e60443cfcceead3c: balance: 3 ether
Sehen Sie sich Infos und Statistiken zu Rinkeby an unter: https://www.rinkeby.io.
Diese Demo zeigt:
Wie Sie einen eigenen Smart Contract in die öffentliche Rinkeby-Blockchain deployen können.
Wie Sie den eigenen Smart Contract in der Rinkeby-Blockchain ausführen können.
Als Smart Contract wird der oben gezeigte MeinToken-Smart-Contract verwendet.
Führen Sie die im Folgenden beschriebenen Schritte aus.
Alle oben unter Öffentliche Rinkeby-Test-Ethereum-Blockchain statt privater Blockchain beschriebenen Schritte müssen ausgeführt worden sein.
Alle oben in dem Beispiel MeinToken-Smart-Contract-Demo beschriebenen Dateien werden benötigt.
Führen Sie folgende Kommandos aus:
cd \MeinWorkspace\EthereumDemo
build-MeinToken.bat
Falls noch nicht geschehen, starten Sie mit der oben erstellten Batchdatei die Rinkeby-Test-Ethereum-Blockchain:
cd \MeinWorkspace\EthereumDemo
starte-Rinkeby-Ethereum-Blockchain.bat
Wahrscheinlich müssen Sie warten, bis die Blockchain synchronisiert ist.
Der eigene Account muss über Ether verfügen:
web3.eth.accounts
loadScript( 'src/checkAllBalances.js' )
Deployen Sie den MeinToken-Smart-Contract:
personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" )
loadScript( 'src/MeinTokenDeploy.js' )
Sie erhalten:
Contract transaction send: TransactionHash: 0x5759... waiting to be mined...
Und nach vielleicht 30 Sekunden:
Contract mined! Address: 0xf07a9248ac043cf039a6f76113b78f89d483b2c8
Falls Sie eine der beiden folgenden Fehlermeldungen erhalten:
Error: exceeds block gas limit
Error: Contract transaction couldn't be found after 50 blocks
Dann stimmt in der Regel etwas mit der angebotenen Gas-Menge nicht. Es darf weder zu wenig noch zu viel Gas angeboten werden. Passen Sie die Variable gasOffer in den Skripten src/meinTokenJsTeil3.txt bzw. src/MeinTokenDeploy.js geeignet an. Falls Sie noch nicht den genauen Wert berechnen wollen, können Sie im ersten Schritt "eth.getBlock( 'latest' ).gasLimit / 2" ansetzen.
Überprüfen Sie die Beauftragung in der Logdatei rinkeby-geth.log. Dort erscheint eine Zeile ähnlich zu:
INFO [02-02|12:13:14] Submitted contract creation fullhash=0x5759... contract=0xF07a...
Erfragen Sie die Adresse des deployten Smart Contracts:
MeinToken.address
Diese 40-stellige Hexadresse müssen Sie sich merken. Speichern Sie die Adresse in die Datei MeinToken.address.Rinkeby.txt.
Führen Sie mit dem deployten Smart Contract einen Transfer von MeinToken durch. Falls noch nicht vorhanden, legen Sie vorher einen zweiten Account an. Überprüfen Sie, dass zumindest der erste Account über Ether verfügt. Lesen Sie vor und nach dem Transfer die MeinToken-Kontostände aus.
personal.newAccount( "Meine Ethereum-Test-Passphrase" )
web3.eth.accounts
loadScript( 'src/checkAllBalances.js' )
web3.eth.defaultAccount = web3.eth.accounts[0]
personal.unlockAccount( web3.eth.defaultAccount, "Meine Ethereum-Test-Passphrase" )
MeinToken.balanceOf( web3.eth.accounts[0] )
MeinToken.balanceOf( web3.eth.accounts[1] )
MeinToken.transfer( web3.eth.accounts[1], 19 );
// Ca. 30 Sekunden auf das Mining warten.
MeinToken.balanceOf( web3.eth.accounts[0] )
MeinToken.balanceOf( web3.eth.accounts[1] )
// Überprüfen Sie, dass der Ether-Kontostand von Ihrem Account um einen geringen Betrag reduziert wurde:
loadScript( 'src/checkAllBalances.js' )
Auch über den öffentlichen Etherscan-Internetdienst können Sie Ihre Transaktionen verfolgen: https://rinkeby.etherscan.io/address/{Meine-Account-Adresse} (ersetzen Sie "{Meine-Account-Adresse}" durch Ihre Account-Adresse).
Mit dem weiter unten unter Analyse der Blöcke und Transaktionen beschriebenem Blöcke-Lese-JavaScript getTransactionsByAccount() können Sie die Transaktionen auch in der Rinkeby-Blockchain verfolgen. Übergeben Sie als Parameter Ihre Account-Adresse, um nicht unter zu vielen anderen Blöcken suchen zu müssen.
Diese Demo zeigt:
Wie Sie den Smart Contract in der Rinkeby-Blockchain mit den oben implementierten DApp-Webseiten für Node.js und für Java verwenden können.
Alle oben unter Smart Contract in die öffentliche Rinkeby-Blockchain deployen beschriebenen Schritte müssen ausgeführt worden sein.
DApp-Webseite für den Smart Contract mit Node.js:
Ersetzen Sie in der Node.js-DApp-Webseite EthNodejsDApp/MeinTokenDAppWebseite.html in der Zeile const contractAddress = "0x..."; die Adresse des MeinToken-Smart-Contracts durch die neue Adresse aus der Rinkeby-Blockchain (gespeichert in MeinToken.address.Rinkeby.txt).
Starten Sie die Node.js-DApp-Webseite:
cd \MeinWorkspace\EthNodejsDApp
start MeinTokenDAppWebseite.html
Sie können wie gewohnt MeinToken-Beträge transferieren:
DApp-Webseite für den Smart Contract mit Java:
Sie können die neue Adresse des MeinToken-Smart-Contracts aus der Rinkeby-Blockchain
und die Passphrase des ersten Accounts wahlweise entweder im Java-Sourcecode
MeinTokenTransfer.java im Verzeichnis
EthJavaDApp\src\main\java\de\meinefirma\meinprojekt
in den beiden Zeilen
public static final String DEFAULT_CONTRACT_ADRESSE = "0x...";
public static final String DEFAULT_PASSPHRASE_OR_PRIVKEY = "...";
eintragen, oder alternativ zur Laufzeit auf der Webseite eingeben.
Starten Sie bei laufender Blockchain den Jetty-Webserver mit der EthJavaDApp-Webanwendung:
cd \MeinWorkspace\EthJavaDApp
mvn jetty:run-war
Warten Sie etwas, bis "Started Jetty Server" erscheint. Rufen Sie die Webseite auf:
start http://localhost:8080/EthJavaDApp
Sie können wie gewohnt MeinToken-Beträge transferieren:
Wie Sie das Truffle-Entwicklungsframework installieren.
Wie Sie mit Truffle eigene Ethereum Smart Contracts erstellen, deployen und ausführen können.
Wie Sie mit Truffle und der Ganache-Test-Blockchain sehr schnelle Tests in JavaScript erstellen können.
Bitte beachten Sie: Diese Demo verwendet nur einen kleinen Bruchteil der vielen Features von Truffle.
Sehen Sie sich die Truffle-Doku an.
Truffle konkurriert mit dem weiter unten vorgestellten Embark.
Verwendet werden folgende Versionen:
nvm 0.33.8
npm 5.6.0
node 9.5.0
Solidity 0.4.19
truffle 4.0.6
ganache-cli 6.0.3
Ubuntu 16.04.3 LTS
Windows 10 Version 1709
Truffle kann mit Linux, Windows und Mac verwendet werden. Da die Installation und Verwendung unter Linux etwas einfacher als unter Windows ist, wird im Folgenden Truffle zwar unter Windows, aber in der bash von WSL (Windows-Subsystem für Linux) verwendet. Falls Sie direkt unter Linux (z.B. Ubuntu) arbeiten, können Sie die ersten Schritte einfach überspringen.
Falls noch nicht geschehen, installieren Sie WSL (Windows-Subsystem für Linux). Voraussetzung hierfür ist Windows 10 in mindestens Version 1709. Rufen Sie die Versionsabfrage auf mit:
winver
Wechseln Sie in Ihr Workspace-Verzeichnis (z.B. \MeinWorkspace) und führen Sie folgende Kommandos aus:
cd \MeinWorkspace
mkdir EthTruffle
cd EthTruffle
Starten Sie die WSL-Bash, führen Sie zur Kontrolle und Orientierung folgende Kommandos aus, und updaten Sie Ihr Linux:
bash
MeinName@MeinPC:/mnt/d/MeinWorkspace/EthTruffle$
cat /etc/issue
Ubuntu 16.04.3 LTS \n \l
pwd
/mnt/d/MeinWorkspace/EthTruffle
sudo apt-get update -y && sudo apt-get upgrade -y
Installieren Sie in der bash den Node Version Manager nvm, Node.js, Truffle und Ganache CLI:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
exec bash
nvm --version
nvm --help
nvm ls
nvm install node
node -v
npm -v
npm install -g truffle ganache-cli
ls -al ~
ls -al ~/.nvm/versions/node/v9.5.0/bin
ls -al ~/.nvm/versions/node/v9.5.0/lib/node_modules
truffle version
truffle init
ls -al
tree
Sie erhalten:
. +-- contracts ¦ +-- Migrations.sol +-- migrations ¦ +-- 1_initial_migration.js +-- test +-- truffle-config.js +-- truffle.js
Ersetzen Sie im EthTruffle-Projektverzeichnis den Inhalt der
Truffle-Konfigurationsdatei
(setzen Sie dabei die korrekte Portnummer ein,
siehe unten):
truffle.js
module.exports = { networks: { development: { host: "localhost", port: 8545, network_id: "*" } } };
Für den zu erstellenden Test soll wieder der Smart Contract aus der oben gezeigten Mini-Token-Smart-Contract: MeinToken-Demo dienen.
Entweder Sie kopieren manuell den Smart-Contract-Solidity-Sourcecode aus dem obigen MeinToken.sol in die neue Datei MeinToken.sol im Unterverzeichnis contracts.
Oder, falls Sie obige Demo im Projektverzeichnis EthereumDemo ausgeführt haben, kopieren Sie die Datei in der bash:
cp ../EthereumDemo/src/MeinToken.sol contracts/
ls -al contracts
Erstellen Sie für den MeinToken-Smart-Contract im migrations-Unterverzeichnis die Deploymentkonfigurationsdatei:
2_MeinToken_migration.js
var MeinToken = artifacts.require( "./MeinToken.sol" ); module.exports = function( deployer ) { deployer.deploy( MeinToken, 999 ); };
Mit der Angabe 999 wird initial der Betrag von 999 MeinToken dem ersten Account gutgeschrieben (Sie können natürlich jeden beliebigen anderen Wert vorgeben).
Erstellen Sie für den MeinToken-Smart-Contract im test-Unterverzeichnis das
JavaScript-Testprogramm:
MeinTokenTest.js
const MeinToken = artifacts.require( "MeinToken" ); contract( 'MeinToken', function( accounts ) { let instance; before( async function () { instance = await MeinToken.deployed(); } ); it( "Initiale MeinToken-Kontostaende: 999 / 0", async function() { balance0 = await instance.balanceOf.call( accounts[0] ); balance1 = await instance.balanceOf.call( accounts[1] ); assert.equal( balance0.valueOf(), 999, "Account 0: Der MeinToken-Kontostand ist nicht 999" ); assert.equal( balance1.valueOf(), 0, "Account 1: Der MeinToken-Kontostand ist nicht 0" ); } ); it( "Transfer von 11 MeinToken, danach: 988 / 11", async function() { await instance.transfer( accounts[1], 11 ); balance0 = await instance.balanceOf.call( accounts[0] ); balance1 = await instance.balanceOf.call( accounts[1] ); assert.equal( balance0.valueOf(), 988, "Account 0: Der MeinToken-Kontostand ist nicht 988" ); assert.equal( balance1.valueOf(), 11, "Account 1: Der MeinToken-Kontostand ist nicht 11" ); } ); } );
Den initialen Betrag 999 müssen Sie an den oben in 2_MeinToken_migration.js eingetragenen Betrag angleichen.
Kompilieren Sie die beiden Solidity-Sourcedateien:
ls -al contracts
truffle compile
ls -al build/contracts/
Sie erhalten zwei .json-Dateien.
Truffle bietet verschiedene Test-Blockchains an, siehe Truffle Ethereum Clients. Mit "truffle develop" würden Sie eine Test-Blockchain mit JavaScript-Console erhalten. Im Folgenden soll stattdessen die üblichere Ganache-Blockchain verwendet werden:
ganache-cli --help
mkdir Ganache-Blockchain
ganache-cli -d -i 66 --db Ganache-Blockchain
Ganache CLI v6.0.3 (ganache-core: 2.0.2) Available Accounts ================== (0) 0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1 (1) 0xffcf8fdee72ac11b5c542428b35eef5769c409f0 ... Private Keys ================== (0) 4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d (1) 6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1 ... ... Listening on localhost:8545
Es wurden automatisch 10 Accounts angelegt, die jeweils über 100 Ether verfügen. Diese Accounts und deren Private Keys werden angezeigt. Kopieren Sie sich zumindest den ersten Private Key, damit Sie weiter unten die Java-DApp-Webseite einsetzen können.
Die verschiedenen mit Truffle startbaren Blockchains verwenden unterschiedliche Portnummern. Deshalb müssen Sie die Ausgabe "Listening on ..." genau beachten. Diese Angabe muss mit der in obiger truffle.js übereinstimmen.
Resultierende Projektstruktur:
Öffnen Sie ein zweites Kommandozeilenfenster, wechseln Sie wieder in die WSL-bash, und sehen Sie sich die resultierende Projektstruktur an:
cd \MeinWorkspace\EthTruffle
bash
tree
. +-- build ¦ +-- contracts ¦ +-- MeinToken.json ¦ +-- Migrations.json +-- contracts ¦ +-- MeinToken.sol ¦ +-- Migrations.sol +-- Ganache-Blockchain ¦ +-- !blockHashes!... ¦ ... +-- migrations ¦ +-- 1_initial_migration.js ¦ +-- 2_MeinToken_migration.js +-- test ¦ +-- MeinTokenTest.js +-- truffle-config.js +-- truffle.js
Automatisierbarer Test des Smart Contracts:
Der Test mit MeinTokenTest.js deployt temporär den MeinToken-Smart-Contract und führt das JavaScript-Testprogramm aus:
truffle test
Using network 'development'. Contract: MeinToken Initiale MeinToken-Kontostaende: 999 / 0 (39ms) Transfer von 11 MeinToken, danach: 988 / 11 (65ms) 2 passing (126ms)
Sie haben erfolgreich Ihren eigenen Smart Contract in die Ganache-Blockchain temporär deployt
und in einem wiederholbaren und automatisierbaren Test ausgeführt und den Smart Contract getestet.
Anders als in anderen Blockchains muss nicht lange auf das Mining gewartet werden.
Deployen des Smart Contracts für manuelle Tests:
Deployen Sie die Smart Contracts für weitere manuelle Tests (z.B. per DApp) in die Ganache-Blockchain:
truffle migrate
Using network 'development'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0xb787070eb6a20cece690a7f9ded231a2a8c74466685156b4d244e745dc7a7b83 Migrations: 0x26b4afb60d6c903165150c6f0aa14f8016be4aec Saving successful migration to network... ... 0x4ae2f8c27694fc1ed60f892e0bd5eaa845a03d8d8a505259c51ce45f6930f5a1 Saving artifacts... Running migration: 2_MeinToken_migration.js Deploying MeinToken... ... 0xd60f0c6b6dd4d26cbfca7d1b735703d1baf43c2fbf3d7446cbe2b52d0e2d754f MeinToken: 0x0e696947a06550def604e82c26fd9e493e576337 Saving successful migration to network... ... 0xe81f630f8d07e4121b0be7d30280de301edab5e28e675aa619a6d8e6f60d85b5 Saving artifacts...
Öffnen Sie die Truffle-Console:
truffle console
Darin können Sie Truffle-JavaScript-Kommandos ausführen, beispielsweise:
web3.eth.accounts
MeinToken.address
MeinToken.at( MeinToken.address ).balanceOf.call( web3.eth.accounts[0] );
MeinToken.at( MeinToken.address ).balanceOf.call( web3.eth.accounts[1] );
Sehen Sie sich weiter unten an, wie Sie den Smart Contract in der Truffle-Test-Blockchain mit einer DApp-Webseite entweder mit Node.js oder mit Java ansprechen können: DApp-Webseite für den Smart Contract sowohl mit Truffle als auch mit Embark.
Wie Sie das Embark-Entwicklungsframework installieren.
Wie Sie mit Embark eigene Ethereum Smart Contracts erstellen, deployen und ausführen können.
Wie Sie mit Embark und einer integrierten Test-Blockchain sehr schnelle Tests in JavaScript erstellen können.
Bitte beachten Sie: Diese Demo verwendet nur einen kleinen Bruchteil der vielen Features von Embark.
Sehen Sie sich die Embark-Doku an.
Embark konkurriert mit dem oben vorgestellten Truffle.
Verwendet werden folgende Versionen:
nvm 0.33.8
npm 5.6.0
node 9.5.0
Solidity 0.4.17
embark 2.6.6
ganache-cli / ethereumjs-testrpc 6.0.3
Ubuntu 16.04.3 LTS
Windows 10 Version 1709
Wie bereits oben mit Truffle wird auch in dieser Demo wieder die bash von WSL (Windows-Subsystem für Linux) verwendet. Falls Sie direkt unter Linux (z.B. Ubuntu) arbeiten, können Sie einige Schritte einfach überspringen.
Falls noch nicht geschehen, installieren Sie WSL, npm und Node.js wie oben für Truffle beschrieben.
Wechseln Sie in Ihr Workspace-Verzeichnis (z.B. \MeinWorkspace):
cd \MeinWorkspace
Starten Sie die WSL-Bash, updaten Sie Ihr Linux, und installieren Sie Embark:
bash
sudo apt-get update -y && sudo apt-get upgrade -y
npm -g install embark ethereumjs-testrpc
ls -al ~
ls -al ~/.nvm/versions/node/v9.5.0/bin
ls -al ~/.nvm/versions/node/v9.5.0/lib/node_modules
embark --version
embark --help
Starten Sie ein neues Embark-Projekt:
embark new EthEmbark
cd EthEmbark
ls -al
tree
Sie erhalten:
. +-- app ¦ +-- contracts ¦ +-- css ¦ +-- index.html ¦ +-- js +-- chains.json +-- config ¦ +-- blockchain.json ¦ +-- communication.json ¦ +-- contracts.json ¦ +-- development ¦ ¦ +-- genesis.json ¦ ¦ +-- password ¦ +-- storage.json ¦ +-- testnet ¦ ¦ +-- password ¦ +-- webserver.json +-- embark.json +-- package.json +-- test +-- contract_spec.js
Für den zu erstellenden Test soll wieder der Smart Contract aus der oben gezeigten Mini-Token-Smart-Contract: MeinToken-Demo dienen.
Entweder Sie kopieren manuell den Smart-Contract-Solidity-Sourcecode aus dem obigen MeinToken.sol in die neue Datei MeinToken.sol im Unterverzeichnis app/contracts.
Oder, falls Sie obige Demo im Projektverzeichnis EthereumDemo ausgeführt haben, kopieren Sie die Datei in der bash:
cp ../EthereumDemo/src/MeinToken.sol app/contracts/
ls -al app/contracts
Ersetzen Sie in der kopierten MeinToken.sol im app/contracts-Unterverzeichnis die Zeile:
pragma solidity ^0.4.19;
durch:
pragma solidity ^0.4.17;
Ersetzen Sie im config-Unterverzeichnis in der Konfigurationsdatei contracts.json die beiden Zeilen:
"contracts": { }
durch:
"contracts": { "MeinToken": { "args": [ 999 ] } }
Erstellen Sie für den MeinToken-Smart-Contract im test-Unterverzeichnis das
JavaScript-Testprogramm:
MeinTokenTest.js
describe( "MeinToken", function() { before( function( done ) { this.timeout( 0 ); var contractsConfig = { "MeinToken": { args: [999] } }; EmbarkSpec.deployAll( contractsConfig, done ); } ); it( "Initiale MeinToken-Kontostaende: 999 / 0", function() { web3.eth.getAccounts( function( err, accounts ) { balance0 = MeinToken.balanceOf.call( accounts[0] ); balance1 = MeinToken.balanceOf.call( accounts[1] ); assert.equal( balance0.valueOf(), 999, "Account 0: Der MeinToken-Kontostand ist nicht 999" ); assert.equal( balance1.valueOf(), 0, "Account 1: Der MeinToken-Kontostand ist nicht 0" ); } ) } ); it( "Transfer von 11 MeinToken, danach: 988 / 11", function() { web3.eth.getAccounts( function( err, accounts ) { MeinToken.transfer( accounts[1], 11 ); balance0 = MeinToken.balanceOf.call( accounts[0] ); balance1 = MeinToken.balanceOf.call( accounts[1] ); assert.equal( balance0.valueOf(), 988, "Account 0: Der MeinToken-Kontostand ist nicht 988" ); assert.equal( balance1.valueOf(), 11, "Account 1: Der MeinToken-Kontostand ist nicht 11" ); } ) } ); } );
Resultierende Projektstruktur:
tree
. +-- app ¦ +-- contracts ¦ ¦ +-- MeinToken.sol ¦ +-- css ¦ +-- index.html ¦ +-- js +-- chains.json +-- config ¦ +-- blockchain.json ¦ +-- communication.json ¦ +-- contracts.json ¦ +-- development ¦ ¦ +-- genesis.json ¦ ¦ +-- password ¦ +-- storage.json ¦ +-- testnet ¦ ¦ +-- password ¦ +-- webserver.json +-- embark.json +-- package.json +-- test +-- contract_spec.js +-- MeinTokenTest.js
Automatisierbarer Test des Smart Contracts:
Der Test mit MeinTokenTest.js startet temporär eine eigene Test-Blockchain, deployt den Smart Contract, führt das JavaScript-Testprogramm aus und beendet die Test-Blockchain wieder:
cd \MeinWorkspace\EthEmbark
bash
embark test
Sie erhalten:
MeinToken deployed contracts Initiale MeinToken-Kontostaende: 999 / 0 Transfer von 11 MeinToken, danach: 988 / 11 2 passing (4s)
Sie haben erfolgreich Ihren eigenen Smart Contract in die Test-Blockchain deployt und in einem wiederholbaren und automatisierbaren Test ausgeführt und den Smart Contract getestet.
Anders als in anderen Blockchains muss nicht lange auf das Mining gewartet werden.
Manueller Test des Smart Contracts mit JavaScript-Kommandos:
Ähnlich wie in der Geth-JavaScript-Console können Sie auch im Embark-Terminal-Dashboard direkt Web3-JavaScript-Kommandos ausführen.
Starten Sie in der bash die in Embark integrierte Test-Blockchain:
embark simulator
Es werden die automatisch angelegten Accounts und deren Private Keys angezeigt. Kopieren Sie sich zumindest den ersten Private Key, damit Sie weiter unten die Java-DApp-Webseite einsetzen können.
Öffnen Sie ein zweites Kommandozeilenfenster, starten Sie die bash, öffnen Sie das Embark-Dashboard, und deployen Sie den Smart Contract:
cd \MeinWorkspace\EthEmbark
bash
embark run
Im Embark-Dashboard (siehe Screenshot) ganz unten im "Console"-Abschnitt können Sie direkt JavaScript-Kommandos ausführen, beispielsweise:
MeinToken.address
MeinToken.balanceOf( web3.eth.accounts[0] )
MeinToken.balanceOf( web3.eth.accounts[1] )
MeinToken.transfer( web3.eth.accounts[1], 19 )
MeinToken.balanceOf( web3.eth.accounts[0] )
MeinToken.balanceOf( web3.eth.accounts[1] )
Live-Blockchain:
Selbstverständlich können Sie Embark nicht nur mit der Embark-Test-Blockchain verwenden, sondern auch mit allen anderen Ethereum-Blockchains. Hierzu verwenden Sie nicht das oben gezeigte Kommando embark simulator, sondern stattdessen embark blockchain.
DApp-Webseite:
Sehen Sie sich im folgenden Kapitel an, wie Sie den Smart Contract in der Embark-Test-Blockchain mit einer DApp-Webseite entweder mit Node.js oder mit Java ansprechen können.
Sie können den in die Truffle- oder Embark-Test-Blockchain deployten Smart Contract mit der oben unter DApp-Webseite für den Smart Contract mit Node.js erstellten DApp-Webseite verwenden.
Ersetzen Sie in der Node.js-DApp-Webseite EthNodejsDApp/MeinTokenDAppWebseite.html in der Zeile const contractAddress = "0x..."; die Adresse des MeinToken-Smart-Contracts durch die neue Adresse aus der Truffle- bzw. Embark-Test-Blockchain, die Sie erhalten, indem Sie in der Truffle- bzw. Embark-Console eingeben:
MeinToken.address
Starten Sie bei laufender Test-Blockchain die Node.js-DApp-Webseite:
cd \MeinWorkspace\EthNodejsDApp
start MeinTokenDAppWebseite.html
Die korrekten MeinToken-Kontostände werden angezeigt. Um MeinToken-Beträge zu transferieren geben Sie als Passphrase ein Minuszeichen ein ("-").
Java-DApp-Webseite:
Auch mit der DApp-Webseite für den Smart Contract mit Java können Sie den in die Truffle- oder Embark-Test-Blockchain deployten Smart Contract verwenden.
Benötigt werden die mit MeinToken.address ermittelte Adresse des MeinToken-Smart-Contracts sowie der Private Key des Sender-Accounts (z.B. des ersten Accounts). Die Private Keys der Accounts werden beim Ausführen von "ganache-cli -d -i 66 --db Ganache-Blockchain" bzw. von "embark simulator" zum Starten der integrierten Test-Blockchain angezeigt.
Sie können die Contract-Adresse und den Private Key wahlweise entweder im Java-Sourcecode
MeinTokenTransfer.java im Verzeichnis
EthJavaDApp\src\main\java\de\meinefirma\meinprojekt
in den beiden Zeilen
public static final String DEFAULT_CONTRACT_ADRESSE = "0x...";
public static final String DEFAULT_PASSPHRASE_OR_PRIVKEY = "...";
eintragen, oder alternativ zur Laufzeit auf der Webseite eingeben.
Starten Sie bei laufender Test-Blockchain den Jetty-Webserver mit der EthJavaDApp-Webanwendung:
cd \MeinWorkspace\EthJavaDApp
mvn jetty:run-war
Warten Sie etwas, bis "Started Jetty Server" erscheint. Rufen Sie die Webseite auf:
start http://localhost:8080/EthJavaDApp
Die korrekten MeinToken-Kontostände werden angezeigt, und Sie können wie gewohnt MeinToken-Beträge transferieren.
Wie externe Informationen beispielsweise per JSON-REST-Schnittstelle abgefragt und in einen Ethereum Smart Contract geladen werden können, obwohl Smart Contracts nicht auf externe Dienste zugreifen können.
Smart Contracts können Programmcode und Berechnungen ausführen und die Ergebnisse in der Blockchain speichern. Aber Smart Contracts können nicht mit der Außenwelt kommunizieren. Für einige Smart Contracts müssen aber Abfragen ausgeführt werden, um bestimmte "Real-World"-Bedingungen überprüfen zu können, beispielsweise Börsenkurse, Flugverspätungen oder Wetterdaten. Als Vermittler ("Data Feed") werden so genannte Oracles programmiert (z.B. in Solidity) und der Blockchain hinzugefügt. Ein Smart Contract kann das Oracle befragen, und das Oracle kann externe Schnittstellen z.B. per REST/JSON abrufen und das Ergebnis per Callback dem Smart Contract übermitteln. Siehe auch: Orakel-Dienste (Christoph Niemann), Types of Oracles, Building an Oracle for Ethereum (John Weldon), Oracles bring data to the blockchain (Jules Dourlens), Ethereum and Oracles (Vitalik Buterin), Oraclize.it: Data Carrier for decentralized Apps.
Einer der bekanntesten Dienstleister für solche Oracle-Dienste ist Oraclize.it. Ein Smart Contract, der diesen Dienst verwendet, wird im Folgenden präsentiert.
Die Verwendung des Oraclize-Dienstes ist mit einer privaten Ethereum-Blockchain etwas aufwändiger, da dann zuerst eine Ethereum-Bridge installiert werden müsste. Deshalb wird hier die Rinkeby-Ethereum-Blockchain verwendet, wo das nicht erforderlich ist.
Falls noch nicht geschehen, starten Sie mit der oben erstellten Batchdatei die Rinkeby-Test-Ethereum-Blockchain:
cd \MeinWorkspace\EthereumDemo
starte-Rinkeby-Ethereum-Blockchain.bat
Wahrscheinlich müssen Sie warten, bis die Blockchain synchronisiert ist.
Der eigene Account muss über Ether verfügen:
web3.eth.accounts
loadScript( 'src/checkAllBalances.js' )
Falls Sie noch keine Accounts angelegt haben: Siehe oben Öffentliche Rinkeby-Test-Ethereum-Blockchain. Falls Sie Accounts haben, aber noch keine Ether: Siehe oben Ether zum Rinkeby-Account transferieren. Falls Sie bereits Ether übertragen haben, aber nach einem Neustart scheint der Ether-Betrag 0 zu sein: Wahrscheinlich müssen Sie einfach länger auf die Blockchain-Synchronisation warten. Ob der Rinkeby-Account über Ether verfügt, überprüfen Sie am besten mit https://rinkeby.etherscan.io/address/{Meine-Account-Adresse} (ersetzen Sie "{Meine-Account-Adresse}" durch Ihre Account-Adresse).
Demonstriert werden soll die Kursabfrage vom britischen Pfund zum Euro. Dazu soll der über folgende REST-URL erreichbare Dienst verwendet werden:
http://api.fixer.io/latest?symbols=USD,GBP
Das JSON-Ergebnis lautet beispielsweise (je nach Webbrowser wird es unterschiedlich formatiert angezeigt):
{ "base":"EUR", "date":"2018-02-13", "rates": { "GBP":0.88935, "USD":1.2333 } }
Als diesen externen Dienst aufrufenden Smart Contract wird die Oraclize-Demo ExampleContract unter https://docs.oraclize.it/#ethereum-quick-start verwendet.
Wie auf der Oraclize-Webseite empfohlen, wird die Remix Online Solidity Compiler IDE verwendet.
Öffnen Sie die Remix Online Solidity Compiler IDE: http://remix.ethereum.org.
Starten Sie ein neues Projekt, indem Sie auf der Webseite oben links auf das kleine Pluszeichen klicken.
Geben Sie bei "File Name" ein: ExampleContract.sol
In dem oberen mittleren leeren Fenster mit dem Titel "browser/ExampleContract.sol" fügen Sie den ExampleContract-Solidity-Sourcecode von https://docs.oraclize.it/#ethereum-quick-start ein (siehe auch den Screenshot weiter unten).
Sehen Sie sich im ExampleContract-Solidity-Sourcecode insbesondere an:
Wie per "import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";" ein weiterer Solidity-Sourcecode importiert wird,
wie in "contract ExampleContract is usingOraclize" der ExampleContract von usingOraclize abgeleitet wird,
wie mit "function __callback(bytes32 myid, string result)" eine Callback-Methode implementiert wird,
und wie mit "oraclize_query("URL", "json(http://api.fixer.io/latest?symbols=USD,GBP).rates.GBP")
die externe JSON-REST-Schnittstelle abgefragt und ein Wert aus dem JSON-Response extrahiert wird.
Klicken Sie im rechten Drittel oben auf den Tabulatorreiter "Compile" und dann auf den Button "Start to compile". Sie erhalten viele orangene Warnungen, aber keine roten Fehlermeldungen.
Klicken Sie auf die Schaltfläche "Details", um die Kompilierergebnisse anzuzeigen. Unter "WEB3DEPLOY" finden Sie ein fertiges JavaScript-Programm zum Deployen des ExampleContract-Smart-Contracts. Dieses JavaScript könnten Sie in der Geth-JavaScript-Console ausführen. In dieser Demo soll jedoch stattdessen alles über die Remix-IDE ausgeführt werden.
Verbinden Sie den Remix Online Solidity Compiler mit Ihrer Rinkeby-Blockchain:
Klicken Sie im rechten Drittel oben auf den Tabulatorreiter "Run",
wählen Sie bei Environment "Web3 Provider",
bestätigen Sie "Are you sure you want to connect to an ethereum node?" sowie
"Web3 Provider Endpoint" "http://localhost:8545".
Oben rechts bei Environment wird angezeigt: "Rinkeby (4)".
Und darunter bei Account erscheint Ihre Account-Adresse und dahinter in Klammern der Ether-Kontostand.
Testen Sie die Verbindung:
Geben Sie im unteren mittleren Fenster ganz unten neben dem ">"-Zeichen Geth-Kommandos ein, beispielsweise:
web3.eth.accounts
und betätigen Sie die Return-Taste.
Deployen Sie Ihren ExampleContract-Smart-Contract in die Rinkeby-Blockchain:
Geben Sie im unteren mittleren Fenster ganz unten neben dem ">"-Zeichen ein:
web3.personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" )
Betätigen Sie im mittleren Teil des rechten Fensters den "Create"-Button.
Sie erhalten beispielsweise:
> web3.personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" ) true creation of ExampleContract pending... [block:1764901 txIndex:2] from:0x39b...ad3c, to:ExampleContract.(constructor), value:0 wei, 2 logs, data:0x606...0029, hash:0xfb1...bda4
Der ExampleContract-Smart-Contract ist erfolgreich in die Rinkeby-Blockchain deployt.
Klicken Sie im unteren mittleren Fenster auf den Details-Button, um sich weitere Informationen anzusehen. Beispielsweise sehen Sie dort die benötigte Gas-Menge.
Wichtig: Merken Sie sich die Contract-Adresse, damit Sie den ExampleContract-Smart-Contract später wieder finden. Klicken Sie rechts neben "ExampleContract at 0x... (blockchain)" auf das Kopier-Icon und speichern Sie die kopierte Contract-Adresse in der Datei Oraclize.ExampleContract.address.Rinkeby.txt.
Fragen Sie den anfänglich ermittelten EUR-GBP-Kurs ab, indem Sie rechts auf den EURGBP-Button klicken. Sie erhalten denselben Wert, wie oben bei der JSON-REST-Abfrage.
Der Zugriff auf die externe JSON-REST-Schnittstelle hat funktioniert.
Führen Sie die updatePrice()-Methode des ExampleContract-Smart-Contracts aus,
indem Sie zuerst einen Unlock ausführen:
web3.personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" )
Und dann rechts auf den updatePrice-Button klicken.
Sie erhalten:
> web3.personal.unlockAccount( web3.eth.accounts[0], "Meine Ethereum-Test-Passphrase" ) true transact to ExampleContract.updatePrice pending ... [block:1764970 txIndex:2] from:0x39...3c, to:ExampleContract.updatePrice() 0xfb...26, value:0 wei, 1 logs, data:0x67...28, hash:0x20...0e
Das Ausführen der updatePrice()-Methode des ExampleContract-Smart-Contracts inklusive erneutem Zugriff auf die externe JSON-REST-Schnittstelle hat funktioniert.
Falls Sie das Glück haben, dass sich der EUR-GBP-Kurs zwischenzeitlich verändert hat, sehen Sie den neuen Wert.
Einfach nur den Kurswert in die Blockchain zu übertragen, macht natürlich keinen Sinn. Aber abhängig vom Kurswert könnte der Smart Contract weitere Entscheidungen treffen oder Aktionen starten. Unter http://dapps.oraclize.it finden Sie Beispiele für "Oraclize-based DApps".
Theoretisch könnte der Oraclize-Dienst die übertragene Information fälschen. Um dies auszuschließen, bietet Oraclize Authenticity Proofs an.
Unter Common useful JavaScript snippets for geth finden Sie einige nützliche Geth-JavaScript-Skripte, beispielsweise das folgende getTransactionsByAccount():
Fügen Sie das folgende Skript bei laufender Blockchain in der Geth-JavaScript-Console ein:
function getTransactionsByAccount(myaccount, startBlockNumber, endBlockNumber) { if (endBlockNumber == null) { endBlockNumber = eth.blockNumber; console.log("Using endBlockNumber: " + endBlockNumber); } if (startBlockNumber == null) { startBlockNumber = endBlockNumber - 50; console.log("Using startBlockNumber: " + startBlockNumber); } console.log("Searching for transactions to/from account \"" + myaccount + "\" within blocks " + startBlockNumber + " and " + endBlockNumber); for (var i = startBlockNumber; i <= endBlockNumber; i++) { if (i % 10 == 0) { console.log("Searching block " + i); } var block = eth.getBlock(i, true); if (block != null && block.transactions != null) { block.transactions.forEach( function(e) { if (myaccount == "*" || myaccount == e.from || myaccount == e.to) { console.log(" tx hash : " + e.hash + "\n" + " nonce : " + e.nonce + "\n" + " blockHash : " + e.blockHash + "\n" + " blockNumber : " + e.blockNumber + "\n" + " transactionIndex: " + e.transactionIndex + "\n" + " from : " + e.from + "\n" + " to : " + e.to + "\n" + " value : " + e.value + "\n" + " time : " + block.timestamp + " " + new Date(block.timestamp * 1000).toGMTString() + "\n" + " gasPrice : " + e.gasPrice + "\n" + " gas : " + e.gas + "\n" + " input : " + e.input); } }) } } }
Anschließend können Sie sich in der Geth-JavaScript-Console bestimmte Transaktionen ansehen, beispielsweise so die letzten Transaktionen für alle Accounts:
getTransactionsByAccount( "*" )
Oder so für einen bestimmten Account in einem bestimmten Bereich von Blocknummern:
getTransactionsByAccount( eth.accounts[0], 100, 200 )
Angenommen Sie erhalten für die beiden letzten Blöcke:
tx hash : 0x7f3ef240d563e843f2cc577d63cc9f9382da8526aff4c7ea565db137c093fc4e nonce : 110 blockHash : 0xeae4e8e1c9b2d73f35fc4f3d5013e37184bbf36ef4b27a303b6fccdcd80f4f4e blockNumber : 9087 transactionIndex: 0 from : 0x4597a26af9991b297b5ccc2a8c0966e9a1a17035 to : 0x63d7d5b64dc9cc0744dedf87971f8b0777d7e226 value : 10000000000000000000 time : 1517595662 Fri, 02 Feb 2018 18:21:02 GMT gasPrice : 22000000000 gas : 4300000 input : 0x tx hash : 0xba9d2cdbe9a1ea65a3551f0d8f39486acb0746f4c2a3ab6f20449eb698e959e5 nonce : 111 blockHash : 0x5964c99158f57252ee97043e267846dc2441472ed766feb89a85698ba19c9514 blockNumber : 9091 transactionIndex: 0 from : 0x4597a26af9991b297b5ccc2a8c0966e9a1a17035 to : 0xa7818f6043df69bcf6c0db5d766e674d034dacfc value : 0 time : 1517595743 Fri, 02 Feb 2018 18:22:23 GMT gasPrice : 22000000000 gas : 4300000 input : 0xa9059cbb00000000000000000000000063d7d5b64dc9cc0744dedf87971f8b0777d7e226000000000000...
Beim vorletzten Block werden 10 Ether (value: 10000000000000000000) vom Account 0x4597... an den Account 0x63d7... transferiert (per EtherTransfer).
Beim letzten Block werden keine Ether transferiert (value: 0). Stattdessen führt der Account 0x4597... den Smart Contract unter 0xa781... aus (input: 0xa905...) (per MeinTokenTransfer).
Falls Sie JetBrains IntelliJ IDEA verwenden, können Sie das Intellij-Solidity-Plugin installieren:
Hinweise zur Installation von IntelliJ IDEA gibt es unter: Install and set up IntelliJ IDEA. Weitere Hinweise finden Sie hier.
Updaten Sie IntelliJ IDEA auf die aktuellste Version.
Zur Installation des Intellij-Solidity-Plugins wählen Sie:
File | Settings... | Plugins | Browse repositories... | Solidity | IntelliJ-Solidity | rechte Maustaste | Download and Install.
Nach dem Neustart von IntelliJ IDEA können Sie Solidity-Dateien erstellen über:
File | New | Smart contract.
In der Version 2.0.4 bietet das Plugin Syntax-Highlighting, Auto-Vervollständigung (Code Completion) und Find Usages.
Dies bietet allerdings auch der Remix Online Solidity Compiler, der darüber hinaus noch viele weitere Features bietet, wie beispielsweise Syntax-Fehler anzeigen, kompilieren, fertigen Web3-JavaScript-Code zum Deployen erstellen (unter Details | WEB3DEPLOY), und sogar Solidity-Code deployen, ausführen und debuggen.
Doku zu Kryptowährungen, Blockchain, Ethereum, Geth, Smart Contracts und Solidity: