Mittwoch, 21. Februar 2018

Ethereum Smart Contracts mit Solidity - eine Kurz-Einführung

Als wäre die Blockchain, die den heute gängigen Kryptowährungen (wie Bitcoin, Ethereum und anderen) zu Grunde liegt, nicht bereits kompliziert genug, rückt Ethereum nun auch noch "intelligente Verträge" (sog. Smart Contracts) ins Licht der Öffentlichkeit.

Viele der bisher im Web verfügbaren Erklärungen dazu sind leider recht unscharf und gerade für Programmierer entweder zu oberflächlich oder gleich ein ausgewachsener Programmierkurs mit nicht unerheblichem Aufwand. Mit diesem Blog-Post möchte ich versuchen, die Mitte zu treffen und zunächst kurz die Idee hinter Smart Contracts erklären, bevor ich ein einfaches Solidity-Beispiel für die Ethereum-Blockchain vorstellen möchte. Es sollte sich inkl. der Pausen zur Validierung der genutzten Test-Blockchain in ca. 60 bis max. 90 Minuten nachvollziehen lassen. Dazu wird außer dem Download des Ethereum-Browsers Mist und eines Social-Media-Account zur Beantragung von Test-Geld im Rinkeby-Netzwerk kein weiterer Aufwand benötigt. Sollten Sie das später gezeigte Programm-Beispiel also selbst ausprobieren wollen, empfiehlt es sich gleich mit dem Download von Mist zu beginnen und dort einen Account im Rinkeby-Netzwerk anzulegen, da die Validierung der dort benutzten Blockchain ca. eine halbe bis dreiviertel Stunde dauern wird.

Bevor wir uns mit weiteren technischen Details befassen, möchte ich noch ein paar Hintergründe zu Smart Contracts in Ethereum erwähnen. Während Bitcoin bis dato nur einen Typ von Accounts anbietet (nämlich die Konten der Nutzer), gibt es in Ethereum einen zweiten Typ: eben die Smart Contracts. Diese können wir uns einfach als Programme vorstellen, die in die Ethereum-Blockchain hochgeladen und dort abgelegt werden. Hochgeladene Programme bekommen eine Adresse, so dass sie durch Ethereum-Transaktionen angestoßen werden können; das geht insbesondere auch von anderen Contracts aus der Blockchain heraus, oder eben durch Überweiseungen, die von Nutzern an einen Contract ausgeführt werden. Einfache Programme bestehen nur aus einem einzelnen Contract, komplexere können auch aus vielen bestehen. Dafür hat sich der Name DApps für Decentralized Apps eingebürgert.

Decentralized Apps
DApps sind im Wesentlichen grob mit Java-Programmen und Contracts wiederum ungefähr mit Java-Klassen vergleichbar, sie können insbesondere auch öffentlich sichtbare Methoden anbieten, die als Einsprungspunkt (also als eine API) für Aufrufe von außen dienen. Sie werden aktuell zumeist in Solidity geschrieben, einer Sprache, die an Javascript angelehnt ist und in der Ethereum Virtual Machine (EVM) ausgeführt werden kann. Um das Ethereum-Netzwerk vor D.O.S.- und anderen Attacken zu schützen, kostet das Ausführen von Smart Contracts Geld oder besser Aufwand, der in diesem Kontext "gas" genannt wird.

Der Zustand eines Contracts, wenn wir so wollen also seine Objektvariablen, wird ebenfalls in der Ethereum-Blockchain gespeichert, wir können Solidity-Programme also im Prinzip beliebig rückwirkend debuggen. A propos Debuggen, ist ein Contract einmal in die Blockchain hochgeladen, verbleibt er dort für die komplette Lebenszeit der Blockchain (er ist immutable), auch wenn er fehlerhaften Code enthalten sollte. Um Fehler auszubessern oder Updates durchzuführen, muss ein neuer Contract hochgeladen werden, der entsprechend eine neue Adresse bekommt.

Als erste Killer-Applikation für DApps gilt übrigens aktuell das Crowdfunding, hier kann mit einigen wenigen Zeilen Code ein Projekt angelegt werden, dessen Einzahlungen nur dann an den Ideengeber ausgezahlt werden, wenn innerhalb einer festgelegten Frist genügend Ether zusammen gekommen sind. Andernfalls fließt das eingezahlte Geld wieder zurück an seinen jeweiligen Geldgeber. Weitere spannende Ideen für DApps finden sich beispielsweise hier.

Accounts anlegen
Kommen wir nun zu einem ersten kleinen Solidity-Programm, das nichts weiter tun soll, als einen eingezahlten Betrag gleichmäßig an zwei anfangs definierte Konten weiterzuverteilen. So ließen sich beispielsweise Taschengeld und Geldgeschenke unter Geschwistern aufteilen.

Sie hatten doch zuvor bereits die Prüfung der Blockchain in Mist angestoßen? Vermutlich läuft sie zwar trotzdem noch, glücklicherweise lässt sich aber bereits währenddessen, beispielsweise mit einem Tweet, die Übertragung von Test-Ether an den in Mist automatisch angelegten Main-Account anstoßen, so dass wir sofort loslegen können, wenn die Prüfung abgeschlossen ist.

Hat das Starten des Mist-Browsers einwandfrei geklappt (evtl. kann der ein oder andere Neustart des Programms notwendig sein, weil es doch nicht ganz reibungslos läuft), können der enthaltenen Wallet (grünes Icon in der Seitenleiste links) verschiedene Konten hinzugefügt und diesen durch einen Klick auf Senden (rechts neben dem Wallet-Icon) Ether überwiesen werden. Das Ergebnis könnte dann etwa folgendermaßen aussehen:


Das erste Solidity-Programm
Nun können wir auch schon mit unserem ersten Solidity-Programm loslegen. Es bietet sich an, den Code nicht direkt in Mist zu editieren (was nach einem Klick auf "Verträge" rechts oben möglich wäre), sondern eine IDE zu verwenden. Auf der Solidity-Website sind zahlreiche Möglichkeiten verlinkt, für unsere erste Annäherung sollte die online verfügbare sogenannte Remix-Umgebung für den Moment vollständig ausreichend sein. Wie auf der folgenden Abbildung zu erkennen ist, erinnert diese stark an gängige IDEs wie Eclipse. Links sind die bisher angelegten Contracts aufgelistet, in der Mitte findet sich der aktuell geöffnete Sourcecode, darunter die Konsolenausgabe und rechts können verschiedene Tabs geöffnet werden, die uns beim Testen und Analysieren der Contracts unterstützen.


Das folgende Programm können Sie einfach aus dem Post nach Remix kopieren oder auch direkt über Mist als neuen Contract in der Blockchain veröffentlichen. Zu letzterem gleich mehr.

pragma solidity ^0.4.18;

contract FairShare {
    address[] receivers = [
        0xFeE2E38AB12c202e82de36975A58d0d9A71Ea4f6,
        0x416e754EcAAD9eEB87f6a242aB15F3272ab6E880];
   
    function FairShare() public payable {
        shareEther();
    }
   
    function () public payable {
        shareEther();
    }
   
    function shareEther() internal {
        uint money = msg.value / receivers.length;
       
        for (uint i = 0; i < receivers.length; i++) {
            receivers[i].transfer(money);
        }
    }
   
}

Zunächst aber noch einige Worte zum Programm-Code selbst. In der ersten Zeile (pragma...) wird die Solidity-Version festgelegt, die unser Contract verwenden soll. contract entspricht in etwa dem "class" in objektorientierten Programmiersprache und gibt unserem Contract seinen Namen.

Danach folgt die Definition eines Arrays mit Namen receivers vom Typ address, das die Kontonummern von zwei Empfänger-Konten enthält, an die alle eingehenden Beträge aufgeteilt weitergeleitet werden sollen.

Ferner enthält der Contract noch drei Funktionen. Die erste entspricht dem Constructor einer Java-Klasse und wird einmalig aufgerufen, wenn der Contract in der Blockchain angelegt wird. Die zweite, namenlose Funktion (der sogenannte Fallback) wird immer dann aufgerufen, wenn eine normale Überweisung ohne Funktionsaufruf an den Contract getätigt wird. Public definiert die öffentliche Sichtbarkeit, im Gegensatz zu internal bei der dritten Funktion und payable gibt an, dass Ether an diese Funktionen geschickt werden können. Dies ist bei der Erstellung eines Contracts nicht zwingend, es kann auch Contracts ohne Ether-Eingang geben. Wenn ein Ether-Eingang vorgesehen ist, sollte man sich auch zwingend darüber Gedanken machen, wie man die Ether wieder aus dem Contract heraus bekommt; wir erinnern uns daran, dass einmal auf der Blockchain verewigte Überweisungen nicht mehr rückgängig zu machen sind.

Offensichtlich leiten die beiden genannten Eingangsfunktionen nur an die dritte Funktion mit Namen shareEther weiter. Diese ermittelt die in den Vertrag eingezahlte Ether-Summe (msg.value) teilt sie in zwei Hälften und schickt diese mit Hilfe von transfer an die beiden in receivers definierten Kontonummern weiter.

Möchten wir das Programm nun lokal testen, ist dies rechts oben im Tab "Run" möglich, wie in der folgenden Abbildung gezeigt.


Wir wählen im Dropdown-Menü in der Mitte das Programm aus, das wir starten möchten und klicken auf "Create". Daraufhin erscheint unten eine Instanz des Programms und die Möglichkeit ihre Funktionen aufzurufen. Da das FairShare-Programm nur den Fallback als öffentliche Methode hat, kann auch nur dieser durch einen Klick unten links aufgerufen werden. Um dem Fallback dabei einen Ether-Betrag zu senden, muss zuvor oberhalb des Dropdown-Menüs ein "Value" eingegeben werden. Ferner kann auch ein lokaler Test-Account gewählt werden, der dafür verwendet werden soll. Das Ergebnis dieser Aufrufe kann jeweils in der grauen Konsolenausgabe unterhalb des Programmcodes begutachtet werden.

Den Contract veröffentlichen
Die Veröffentlichung des Contracts ist mit Hilfe des Mist-Browsers nicht weiter schwer. Zunächst ist dafür ein Klick auf "Verträge" und dann ein Klick auf "Neue Verträge Veröffentlichen" notwendig, woraufhin folgende Seite erscheint.


Auf dieser kann ausgewählt werden, von welchem Account aus der Contract veröffentlicht werden soll und ob dem Contract gleich bei seiner Initialisierung Ether übermittelt werden sollen. Der eben in der Remix-Umgebung erstellte Programmcode kann nun hierhin kopiert und mit einem weiteren Klick in der Blockchain veröffentlicht werden. Der Vertrag erhält dann eine eigene Adresse und erscheint in der Auflistung der eigenen Verträge im Mist-Browser, wie im Folgenden gezeigt:


Der von mir in die Blockchain gestellte Contract hat beispielsweise die Adresse 0x63e37aFb51DD5BD30DAbcee135B747D745980634 im Rinkeby-Netzwerk erhalten. Wenn Sie mögen, können Sie probehalber gerne einmal Test-Geld dorthin überweisen, das dann an die beiden im Code gezeigten Accounts weiter verteilt wird.
Ein Klick auf den Vertrag bringt noch einige weitere Detailinformationen zum Vorschein, die an dieser Stelle allerdings recht uninteressant sind, da der Contract über keinerlei öffentliche Funktionen und auch über keinerlei Ether verfügt, da alle eingehenden Beträge direkt wieder ausgezahlt werden. Ich plane daher, den in der Abbildung ebenfalls zu sehenden Lottery-Contract bei nächster Gelegenheit hier vorzustellen und dabei auch ein wenig auf das Debugging in Solidity einzugehen.