Während die meisten öffentlichen Kryptowährungen alle recht ähnliche Peer-to-Peer-Ansätze verfolgen, deren Transaktionen sich grob in Bitcoin- oder Ethereum-ähnlich einteilen lassen, gehen private Blockchains teilweise radikal andere Wege. In diesem Artikel wollen wir die beiden Platzhirsche Hyperledger Fabric und Corda miteinander vergleichen. Kenntnisse mindestens einer der beiden ist zum Verständnis hilfreich. Als repräsentatives Fallbeispiel einer öffentlichen Blockchain mit Smart Contracts soll Ethereum dienen.
Installation
Ein Ethereum-Netzwerk wird dadurch aufgespannt, dass eine beliebige Menge von Knoten mit vergleichbarer Konfiguration an beliebigem Ort gestartet wird. Alle Knoten sind gleichberechtigt und können unterschiedliche Implementierungen des Protokolls benutzen. Fabric und Corda hingegen bestehen aus verschiedenen Arten von Knoten, für die es auch bis dato nur jeweils eine Implementierung gibt. Diese Knoten haben unterschiedliche Aufgaben und Berechtigungen.
Für Fabric sind das diese Komponenten:
- Clients: möchten Transaktionen durchführen lassen.
- Peers: speichern die Daten der Blockchain; spezielle Peers (so-genannte Endorsers) validieren Transaktionen, führen zugeordneten Chaincode (Smart Contracts) aus, signieren das Resultat.
- Ordering Service: Infrastruktur, die signierte Transaktionsergeb-nisse reihen und zu Blöcken zusammenfassen; meist Kafka und ZooKeeper.
- Membership Service Provider: stellen Authentifizierung für Peers und Orderers bereit.
Bei Corda besteht eine Installation aus den folgenden Komponenten:
- Notary Service: verhindert „double spending”, überprüft Zeitfenster für Transaktionen und kann optional auch Transaktionen validieren.
- Node: eindeutige Identität innerhalb des Netzwerks und dient als Ablaufumgebung für CorDapps (Corda Distributed Application).
- Network Map Service: optionaler Dienst, der für die Adressierung von Nodes zuständig ist. Bei Fabric und Corda sind diese Komponenten aus Betriebssicht jeweils unabhängig voneinander.
Datenmodell
Fabric verwaltet als Zustand des Systems eine Menge von Tripeln: Schlüssel, Wert und Version. Wenn ein Schlüssel mit einem neuen Wert überschrieben wird, dann muss die Version inkrementiert werden. Wenn während einer Transaktion aus einem Schlüssel gelesen wird, der von einer parallelen Transaktion geschrieben wird, dann zählt das Lesen des Schlüssels durch die erste Transaktion als veraltet (stale read). Die Smart Contracts in Fabric haben keinen Zustand, sondern greifen ausschließlich auf die Schlüssel/ Wert-Daten zurück. Fabric ist daher vergleichbar mit einer verteilten Key-Value-Datenbank, wobei diese Key-Value-Paare auf den Peers gespeichert werden.
In Corda wird Zustand durch unveränderliche Objekte repräsentiert, die beliebige Daten enthalten können und auf dem Ledger als shared facts gespeichert werden. Soll Zustand verändert werden, so wird ein neue Version des Objekts erzeugt und die alte Version als historisch markiert. Contracts in Corda haben ebenfalls keinen Zustand, sondern greifen nur auf die Daten zu, die durch die Transaktion zur Verfügung gestellt werden. Dies entspricht grob dem UTxO-Modell von Bitcoin.
SmartContracts & Applikationen
In Fabric werden Smart Contracts als Chaincode bezeichnet. Sie können in einer beliebigen Sprache geschrieben sein und laufen auf den Endorser-Knoten in Docker-Containern. Gängige Sprachen sind Java, JavaScript und Go. Für die Makroarchitektur von Applikationen macht Fabric keine weiteren Vorgaben; diese können in traditionelle Architekturen und Systeme eingegliedert werden.
Bei Corda hingegen handelt es sich um eine Plattform, auf der sogenannte CorDapps betrieben werden können. Dabei handelt es sich um verteilte Anwendungen, die in Java oder Kotlin geschrieben werden und auf der JVM laufen. Smart Contracts sind dabei nur ein Aspekt einer CorDapp. Sie verifizieren Transaktionen und stellen somit sicher, dass die Geschäftsregeln eingehalten werden. Ein weiterer Bestandteil einer CorDapp ist der Flow, der für die Koordination des Ablaufs zwischen den beteiligten Parteien verantwortlich ist. Außerdem gehören folgende Bestandteile dazu:
- States repräsentieren Fakten auf dem Ledger. Jeder Knoten besitzt einen Vault, in dem alle relevanten States gespeichert sind.
- Static Web Content wie HTML-Seiten, JavaScript-Libraries und Assets.
Eine Applikation wird auf einen Node deployt und kann folgende Dienste der Laufzeitumgebung in Anspruch nehmen:
- Identity Service: dient der Ermittlung eines Knotens, mit dem kommuniziert werden soll; zudem synchronisiert sich dieser Service mit der Network Map, in der diese Informationen gespeichert sind.
- Key Management Service: ermittelt geeignete private Schlüssel, um eine Transaktion zu signieren, und erzeugt neue Schlüssel, wenn diese in Flow benötigt werden.
- Artemis Messaging Service: ist als AMQP-Broker für die zuverläs-sige Kommunikation verantwortlich.
- Network Map Service: verwaltet die registrierten Nodes in einem Netzwerk.
- DB Services: persistieren Flows und Transaktionen; somit wird ein Neustart einer Anwendung möglich, die dann dort fortgesetzt wird, wo sie gestoppt wurde.
- Attachment Service: dient dem Hinzufügen von Dokumenten zu einer Transaktion; die Daten werden persistiert und können mittels eingebauten Webserver gelesen werden.
- Vault Service: speichert noch nicht verarbeiteten State.
- Web Server: ein eingebetteter Jetty-Server; produktive Anwen-dung benutzen aber eher Spring Boot und greifen auf CorDapps mittels RPC-Client zu.
Beiden Systemen ist gemein, dass sie General Purpose Languages für Smart Contracts benutzen. Insbesondere kann man dadurch auf Umgebungszustand zugreifen und damit nichtdeterministisches Verhalten erzeugen (z. B. Zufallswerte). Dies steht im starken Kontrast zu Ethereum, wo Operationen nur einen stark kontrollierten Zugriff auf Zustand außerhalb des Ledgers haben, zum Beispiel auf die aktuelle Uhrzeit. Corda versucht, dieses Problem durch eine deterministische JVM zu lösen, die aber derzeit noch in der Experimentierphase ist.
Transaktionen
In Ethereum besteht eine Transaktion aus Ziel- und Quelladresse, Betrag und Payload. Anschließend wird diese von allen teilnehmenden Knoten ausgeführt. Konsens wird durch die „ganze Welt” gebildet, was Ethereum den Beinamen World Computer eingebracht hat.
Im Unternehmenskontext ist das im Regelfall nicht erwünscht; Smart Contracts sollten lokal und zur Einsparung von Rechenzeit möglichst nicht redundant ausgeführt werden. Daher sind Transaktionen sowohl bei Fabric als auch Corda komplizierter ausgestaltet. Eine Transaktion in Fabric verfügt über keine formelle Struktur. Im Regelfall kann man sie sich aber als Methodenaufruf (RPC) vorstellen. Die Ausführung folgt einem mehrstufigen Prozess (s. Abb. 1).
Abb. 1: Stufen der Transaktionsausführung bei Fabric
Stufe 1 Chaincode execution: Ein Client reicht eine Transaktion ein. Dazu muss er einer bestimmten Teilmenge der Peers (welche Teilmenge ist durch Konfiguration festgelegt) diese Transaktion präsentieren. Das Ergebnis einer Transaktion besteht aus zwei Komponenten:
- eine Liste der gelesenen Schlüssel und ihre Versionen zum Zeitpunkt des Lesens und
- eine Liste zu schreibender Schlüssel.
Stufe 2 Endorsement collection: Die Peers protokollieren alle Lese- und Schreibvorgänge, führen aber die Schreibvorgänge nicht aus. Lediglich in dem Transaktionsresultat sind die neuen Werte der Schlüssel ersichtlich. Wenn ein Peer dieses Resultat produziert hat, sendet er es signiert zum Client zurück. In diesem Zusammenhang wird dann ein Peer zu einem Endorser.
Stufe 3 Transaction Submission: Der Client muss nun warten, bis er genügend viele validierte Transaktionsresultate gesammelt hat. Für den Client ist es nicht nachvollziehbar, welcher Chaincode gelaufen ist; er erhält nur eine Sicht auf die Ergebnisse. Da der Chaincode als Docker-Image beliebige Operationen durchführen kann, zum Beispiel auch externe Ereignisse abfragen, kann es durchaus sein, dass sich die gesammelten Resultate voneinander unterscheiden. In diesem Fall muss der Client die Resultate verwerfen und die Transaktion neu starten. Eine erfolgreiche Beendigung der Transaktion ist nicht garantiert.
Stufe 4 Delivery: Wenn der Client übereinstimmende Resultate gesammelt hat, dann werden diese an den Ordering-Service geschickt. Dieser sammelt mehrere Transaktionen und bildet daraus einen Block.
Stufe 5 Validation: Sobald der Block abgeschlossen ist, wird er an die Peers propagiert, die daraufhin ihren internen Zustand aktualisieren, nachdem sie den Block erneut validiert haben. Eine Transaktion wird ungültig, wenn sie veraltete Lesezugriffe durchgeführt hat oder der Client nicht genügend viele Endorsers angefragt hatte. Diese Semantik ähnelt stark dem Konzept desSoftware Transactional Memory. Sofern die Transaktion ungültig ist, wird sie von den Endorsers verworfen, bleibt aber trotzdem im Block enthalten. Der Client muss die Transaktion neu starten. Auch hierbei ist die erfolgreiche Beendigung nicht garantiert.
Ähnlich wie in Bitcoin nehmen Transaktionen in Corda einen oder mehrere Input-States entgegen (oder auch keine) und erzeugen daraus keine oder mehrere Output-States. Weiterhin enthält sie folgende Komponenten (s. Abb. 2):
- Attachments: Anhänge in einer ZIP-Datei, wie AGBs, Vertragserläuterungen.
- Commands: beschreiben die Absicht der Transaktion, wie bezah-len oder kaufen. Zudem sind die Public Keys der an der Transaktion beteiligten Parteien beigefügt.
- Time Window: gibt die Gültigkeit der Transaktion an.
- Signaturen: der beteiligten Parteien an der Transaktion und des Notary Service.
Abb. 2: Komponenten der Transaktion in Corda
In Corda werden Transaktionen nicht global versendet, sondern immer zwischen einer definierten Anzahl von Parteien durchgeführt. Für die Koordination dieses Workflows ist der Flow der CorDapp zuständig. Dieser gewährleistet wann, wer und was jemand präsentiert bekommt.
Nachdem das Transaktionsobjekt erzeugt und signiert wurde, wird es durch den implementierten Workflow dem Vertragspartner zugestellt, der seinerseits die Transaktion verifiziert. Sollte das Ergebnis positiv sein, so wird dieser seine Signatur an die Transaktion hängen.
Wenn nun alle beteiligten Parteien die Transaktion signiert haben, wird diese dem Notary Service zugestellt, der folgende Prüfungen durchführt.
- Wurde das angegebene Zeitfenster für die Transaktion eingehalten?
- Ist die Transaktion durch alle beteiligten Parteien signiert worden?
- Sind von allen beteiligten Parteien die Public Keys vorhanden?
- Wurde der Input-State bereits in anderen Transaktionen ver-wendet (double spend)?
- Optional kann der Notary Service auch die Transaktion selbst noch einmal validieren, dazu benötigt dieser aber den vollen Zugriff auf die Daten der Transaktion.
Wenn alle Prüfungen erfolgreich durchgeführt wurden, so signiert der Notary Service diese Transaktion und gibt diese Signatur zurück. Zudem wird der Input-State als historisch gekennzeichnet und somit wird der Output-State der aktuell gültige State in dem Ledger. Abbildung 3 zeigt noch einmal den Verlauf einer Transaktion.
Abb. 3: Verlauf einer Transaktion
Skalierbarkeit
Fabric versucht eine höhere Skalierbarkeit dadurch zu erreichen, dass die Ausführung von Transaktionen und der Commit ihrer Ergebnisse in die Blockchain voneinander entkoppelt sind. Die Ausführung des Chaincodes lässt sich sehr einfach vertikal skalieren, indem man einen Load-Balancer zwischen Client und Validator schalten kann.
Corda erreicht eine höhere Skalierbarkeit dadurch, dass Transaktionen nicht global an alle Teilnehmer des Netzwerkes verteilt werden, sondern nur an die beteiligten Parteien einer Transaktion.
Datenschutz
In Fabric kann man mehrere Blockchains auf der gleichen Infrastruktur betreiben. Untereinander können diese Chains nicht kommunizieren. Separierung wird mittels Topics in Kafka erzielt.
In Corda teilen sich Parteien nur die Daten, die für die beteiligten Parteien vorgesehen sind. Es gibt keinen globalen Ledger, bei dem alle Daten auf allen Nodes gespeichert werden.
Fazit
Sowohl Fabric als auch Corda haben einige andere Ansätze als klassische öffentliche Blockchain-Technologien. Während in Fabric Blöcke auch ungültige Transaktionen enthalten können, hat Corda erst gar keine Blöcke. Daher wird oft statt von Blockchain von Distributed Ledgers gesprochen. Beide Systeme lassen sich mit Java beziehungsweise JVM-Sprachen programmieren und bieten große Freiheiten in der Architektur und Implementierung von Anwendungen. Im Betrieb sind sie beide etwas aufwendiger als zum Beispiel Ethereum, da es verschiedene Arten von Knoten mit teilweise erhöhten Anforderungen an die Umgebung gibt. Letztlich muss anhand des konkreten Anwendungsfalls entschieden werden, ob Fabric oder Corda besser geeignet ist.
Die Autoren bedanken sich bei Christian Koller und Ingo Rammer für Kommentare zur Verbesserung dieses Artikels.
Links
[Corda] An Open Source Blockchain Platform for Businesses, https://www.corda.net
[Fabric] Hyperledger Fabric, https://www.hyperledger.org/projects/fabric