Dieser Artikel setzt Grundkenntnisse zu Bitcoin voraus. Leser*innen sollten mit den Begrifflichkeiten Schlüssel(paar), Signatur, Bitcoin, Mining, Blöcke und Blockchain vertraut sein. Dazu eignet sich der Blockchain-Podcast mit Stefan Tilkov und Lucas Dohmen hervorragend. In Teil 2 dieser Serie geht es weiter mit Ethereum und Teil 3 dreht sich um Altcoins.

Geld und Geldbeutel

In einem klassischen Geldsystem gibt es zwei Objekte, mit denen man Geld verwalten kann. Zum einen sind das Geldbeutel, in denen Menschen Münzen und Geldscheine mit sich tragen. Eine Transaktion besteht dann darin, dass ein Mensch einem anderen Menschen physikalische Objekte überreicht. Geldbeutel als solche können im Prinzip beliebig viel Geld beinhalten und beliebig viele Transaktionen ausführen; können aber auch selbst den Besitz wechseln.

Zum anderen gibt es die Banken, die Konten anbieten. Ein Konto kann zu einem Menschen oder einer Organisation gehören. Der Einfachheit halber werde ich in diesem Artikel generell nur von Subjekten sprechen, da es irrelevant ist, ob es sich um eine natürliche oder rechtliche Person handelt. Die Aufgabe einer Bank ist es, Konten zu verwalten, die virtuelles Geld beinhalten. Ähnlich wie bei einer Geldbörse können Konten beliebig viel Geld beinhalten und beliebig viele Transaktionen ausführen. Konten werden über Kontonummern (hierzulande IBAN) identifiziert. Überweisungen spezifizieren eine Absender- und eine Empfängerkontonummer. Die Bank, die das Absenderkonto führt, verlangt vom Subjekt eine Legitimation, z.B. durch ein PIN-TAN-Verfahren. Dieser Vorgang bleibt allerdings geheim. Für das Empfängerkonto ist es irrelevant, wie die Legitimation abläuft. Das Geld als solches wird durch Einträge in einer Datenbank repräsentiert.

Bei Kryptowährungen funktionieren sowohl die Transaktionen als auch die „Münzen“ völlig anders, sodass weder Konten noch Bargeld als Analogie gut funktionieren. Stattdessen wird als Begriff im Regelfall Adresse benutzt.

Grundsätzlich unterstützen die gängigsten Kryptowährungen Adressen, die aus einem Schlüsselpaar (private und öffentlich) erzeugt werden. Die Adresse wird aus dem öffentlichen Teil des Schlüssels abgeleitet. Transaktionen müssen mit dem privaten Schlüssel der Quelle signiert sein. Üblicherweise legt ein Subjekt eine Adresse an, indem es einfach ein Schlüsselpaar erzeugt und den öffentlichen Schlüssel geeignet publiziert. Die Details zwischen den verschiedenen Systemen unterscheiden sich aber teils deutlich.

Um genau zu sein, ist es außerdem so, dass per se keine Adressen angelegt werden können, sondern alle Adressen bereits existieren. Kontrolle über eine Adresse A erlangt man, indem man einen private Schlüssel findet, dessen öffentlicher Teil genau A entspricht. Mit diesem privaten Schlüssel müssen ausgehende Transaktionen von A signiert werden, um vom Blockchain-Netzwerk akzeptiert zu werden.

Verliert man diesen privaten Schlüssel, oder existiert vielleicht ein solcher Schlüssel nicht, dann laufen Transaktionen an A faktisch ins Leere, denn niemand kann jemals (mehr) ausgehende Transaktionen signieren.

Manche Kryptowährungen unterstützen zusätzlich dazu andere Arten von Adressen, die nicht zwingend von einem Schlüsselpaar herrühren. Dafür hat sich mittlerweile der Begriff Smart Contract etabliert, obwohl es da zahlreiche Varianten gibt, die sich in ihren Möglichkeiten unterscheiden.

Im folgenden möchte ich die Gemeinsamkeiten und Unterschiede zwischen den beiden größten Kryptowährungen Bitcoin und Ethereum herausarbeiten. Später werde ich noch auf anderere Kryptowährungen eingehen.

Bitcoin

Das Grundprinzip einer Transaktion bei Bitcoin besteht darin, eine oder mehrere Quellen auf eine oder mehrere Senken zu verteilen. Oftmals beinhaltet eine Transaktion jedoch nur eine Quelle. Die Quelle ist hierbei immer die Senke einer früheren Transaktion. Senken sind Adressen, denen eine bestimmte Menge an Bitcoin übertragen wird.

Angenommen, es gibt eine bereits abgeschlossene Transaktion T, die als Senke für 1 Bitcoin die Adresse A angegeben hat. Die Quelle von T ist für das Beispiel nicht relevant. Nun kann das Subjekt, welches A kontrolliert, eine neue Transaktion U veranlassen, die den Gesamtbetrag von T in Höhe von 1 Bitcoin an weitere Senken verteilt. Die wichtigste Einschränkung ist, dass es nur eine einzige Transaktion geben kann, die den Betrag aus der Senke A der Transaktion T weiter verteilt. Solch eine Transaktion muss mit dem privaten Schlüssel von A signiert sein.

Wechselgeld

Angenommen, A möchte B 0,5 Bitcoin zukommen lassen und signiert eine solche Transaktion T. Dann blieben die restlichen 0,5 Bitcoin unverteilt, denn die Transaktion muss immer den Gesamtbetrag der Quelle verteilen. Würde man solch eine Transaktion absenden, so hätte die Adresse A keine Kontrolle mehr über diese übrigen Bitcoin: sie gehen verloren. Stattdessen muss man eine neue Adresse C erzeugen, die unter der eigenen Kontrolle steht, an die das Wechselgeld übertragen wird. U muss also so aussehen:

Quelle
(T, A); Summe: 1 Bitcoin
Senke 1
0,5 Bitcoin an B
Senke 2
0,5 Bitcoin an C

Grafisch dargestellt:

Überweisung von A an B und C
Überweisung von A an B und C

Damit ist der komplette Wert der Senke A der Transaktion T verbraucht. Die Subjekte, die B (jemand anderes) und C (man selbst) kontrollieren, können nun unabhängig voneinander über jeweils 0,5 Bitcoin verfügen. In Bitcoin-Terminologie nennt man diese beiden noch ungenutzten Senken Unspent Transaction Outputs (UTXOs), die es irgendwann in Anspruch zu nehmen gilt.

Diesen Mechanismus zu verstehen ist essentiell, um beim Handel mit Bitcoin keinen versehentlichen Verlust zu erleiden. Im Gegensatz zu einer Banküberweisung, wo der Betrag einfach vom Konto abgezogen wird, hört bei Bitcoin die Quelle auf zu existieren, nachdem eine einzige Transaktion durchgeführt worden ist. Gängige Client-Software erzeugt automatisch eine frische Adresse, an die das Wechselgeld überwiesen wird.

Warum muss C aber eine frische Adresse sein? Kann nicht C = A gelten? Technisch ist das möglich, aber es ist eine schlechte Idee, wie ich später erklären werde. Mastering Bitcoin abstrahiert an dieser Stelle und erklärt, dass Wechselgeld explizit an das ursprüngliche Subjekt beziehungsweise die ursprüngliche Wallet zurückfließt.

Mögliche Analogien

Bemüht man den Vergleich zu Papiergeld, so wäre das in etwas so, als ob eine Banknote zerrissen würde und die entstandenen Teile jeweils einen eigenen Wert und Seriennummer hätten. Die ursprüngliche Banknote existiert nicht mehr, dafür zwei neue Banknoten, deren kombinierter Wert dem ursprünglichen Wert entspricht.

Als Java-Code ausgedrückt, kann man sich Transaktionen wie folgt vorstellen:

interface Transaction {
    // transaction id
    String getTxnId();

    // transaction sources: pairs of (transaction, sink)
    // assuming no address is used twice
    Map<Transaction, Address> getSources();

    // valid signature for each source address
    Signature getSignature(Address source);

    // transaction sinks: pairs of (address, value)
    Map<Address, Value> getSinks();
}

Man erkennt dabei schön, wie dadurch eine Kette von Transaktionen aufgespannt wird. Um genau zu sein, handelt es sich dabei um einen gerichteten azyklischen Graphen, denn eine Transaktion V dürfte B und C als Quellen angeben, sofern sie von beiden signiert ist:

Überweisungsgraph
Überweisungsgraph

In dieser vereinfachten Darstellung könnte der Graph tatsächlich zyklisch werden, wenn eine Adresse in mehr als einer Transaktion als Quelle benutzt würde. Daher muss man sich vor Augen führen, dass die Knoten jeweils Paare aus Transaktion und Senke sind und ein Knoten nicht durch lediglich eine Adresse repräsentiert wird. Eine Adresse kann demnach mehrfach im Graphen vorkommen.

Wertschöpfung

Bisher habe ich bequemerweise weggelassen, woher Transaktion T ihren Quellbetrag nimmt. Verallgemeinert stellt sich also die Frage, wie Wertschöpfung in Bitcoin funktioniert. Ich möchte dieses Prinzip hier kurz anreißen.

Jeder Block darf genau eine sogenannte Coinbase-Transaktion enthalten. Angenommen, es gibt eine bestimmte Menge an Transaktionen, die derzeit noch nicht in einem Block enthalten sind. Ein Miner mit der Adresse M, der gerade auf der Suche nach einem Block ist, stellt dieser Transaktionsmenge eine eigene Transaktion W voran:

Coinbase-Transaktion
Coinbase-Transaktion

Diese Transaktion hat keine Quellen. Sie erzeugt Bitcoin in der Höhe der aktuellen Block-Belohnung für Miner (Stand Februar 2019: 12,5 Bitcoin).

Wiederverwendung von Adressen

Eine Mehrfachnutzung von Adressen ist unbedingt zu vermeiden. Dafür gibt es verschiedene Gründe. Am wichtigsten ist es, sich noch einmal vor Augen zu führen, dass Bitcoin keine anonymen Transaktionen kennt: die Quellen- und Senken-Adressen sind in der Blockchain – dem öffentlichen Bilanzheft – ersichtlich. Es handelt sich daher nur um pseudonyme Transaktionen, ähnlich einem Nutzernamen, den man sich in einem Forum gibt.

Wenn man nun eine Adresse mehrfach benutzt, so können durch Kreuz-Referenzierung mehrerer Transaktionen Rückschlüsse auf die Identität des Subjekts, die eine Adresse kontrolliert, gezogen werden.

Als Praxisbeispiel: Man bezahlt ein Produkt im Handel mit Bitcoin. Der Händler erfährt die eigene Bitcoin-Adresse und kann nachvollziehen, für welche Beträge man anderswo eingekauft hat. Umgekehrt könnte man aber auch selbst nachvollziehen, welche Einnahmen ein Händler hat.

Das Bitcoin-Wiki führt hierzu aus:

There has been significant research into the area of what researchers are calling ‚identity collapse‘, which is what happens when more than one Bitcoin address is strongly-linked via the Bitcoin transaction graph to another. Re-using addresses makes their job trivial. There are publically-known databases that exist, right now, that have not only collapsed millions of Bitcoin addresses, but used publically-available information to link those collapsed identities to individuals, and these databases are being actively maintained.

Letztendlich sollte man sich eine Bitcoin-Adresse eher als Wegwerf-Adresse vorstellen.

Glücklicherweise lässt einen Bitcoin mit diesem Problem nicht allein. BIP 32 hat ein praktikables Verfahren für deterministische hierarchische Wallets standardisiert. Eine Wallet steht im Sprachgebrauch sowohl für eine Ansammlung von Adressen, als auch für Client-Software, mit der sich diese verwalten lassen.

Die Grundidee von deterministischen hierarchischen Wallets lässt sich durch folgende Anforderungen spezifizieren:

  1. Es gibt einen Seed, der aus einer Reihe von Wörtern einer natürlichen Sprache bestehen (spezifiziert in BIP 39).
  2. Aus dem Seed kann man beliebige Adressen erzeugen. Man braucht sich dafür nur den Seed zu merken oder zu notieren.
  3. Für Drittsoftware ist es auch möglich, Adressen zu erzeugen, ohne den Seed selbst zu kennen, sondern nur einen Teil vom Seed.

Für einfache Geschäfte genügt es also, zufällig einen Seed generieren zu lassen und dann fortlaufend neue Adressen zu benutzen. Komplexere Situationen lassen sich auch abbilden: Angenommen, man möchte einen Spendenbutton auf einer Webseite betreiben und vorbildlicherweise keine Adresse mehrfach benutzen. Trotzdem sollte der Webserver keinen Zugriff auf den Seed haben, da sich daraus die privaten Schlüssel erzeugen lassen; eine Sicherheitslücke hätte dann fatale Folgen. Stattdessen kann Wallet-Software aus dem Seed einen Unterschlüssel erzeugen, mit dem der Webserver zwar Adressen erzeugen kann, der sich aber selbst nicht für die Signierung von Transaktionen benutzen lässt. So kann eine Webseite für jede Zahlung eine neue Adresse anzeigen, die man dann über den Seed, der nur auf dem lokalen Host gespeichert ist, abrufen kann.

BIP 32 führt die nötigen kryptografischen Details aus, welche Operationen auf sogenannten elliptischen Kurven ausnutzen.

Skripte

Tatsächlich ist die weiter oben angegebene Definition von Senke etwas zu kurz gegriffen. Die Senke besteht nicht aus einem Betrag und einer Adresse, sondern aus einem Betrag und einem Skript. Dieses Skript ist ein Programm in einer Stack-basierten, Forth-artigen Programmiersprache. Die Bitcoin-Spezifikation definiert eine ganze Reihe von Operatoren, die den Stack manipulieren können. Eine Transaktion, die eine Senke benutzen möchte, muss Eingabewerte so vorgeben, dass das Skript ohne Fehler läuft.

Eine akkuratere Fassung der Transaction-Struktur sieht also so aus:

interface Transaction {
    // transaction id
    String getTxnId();

    // transaction sources: pairs of (transaction, sink)
    // assuming no address is used twice
    Map<Transaction, Address> getSources();

    // valid input for each source
    List<String> getScriptInput(Address source);

    // transaction sinks: pairs of (address, script)
    Map<Address, List<Instruction>> getSinks();
}

In den allermeisten Fällen folgt ein Senken-Skript einem festen Schema, z.B. bei der Transaktion T:

OP_DUP OP_HASH160 <A> OP_EQUALVERIFY OP_CHECKSIG

Hier steht A wieder für die Adresse der Senke. Formal gesehen muss man noch hinzufügen, dass die Adresse nicht direkt dem öffentlichen Schlüssel entspricht, sondern nur der Hash davon ist. Doch dazu später mehr.

Betrachten wir nun Transaktion U. Die Quelle von U besteht nun nicht nur aus der Referenz auf T, sondern auch noch aus Eingaben für deren Skript. Im Regelfall ist dies die Signatur Sig und der öffentliche Schlüssel Kpub, wobei A = hash(Kpub) gilt. Die Signatur S wird aus einer vereinfachten Version von U und dem privaten Schlüssel Kpriv gebildet.[1] Der Stack enthält zu Beginn Sig und Kpub. Die folgende Tabelle beschreibt schrittweise die Validierung der Transaktion U:

Stack–Zustand Operation Erklärung
[Sig, Kpub] (keine) Startzustand
[Sig, Kpub, Kpub] OP_DUP oberstes Element wird dupliziert
[Sig, Kpub, hash(Kpub)] OP_HASH160 oberstes Element wird gehasht
[Sig, Kpub, hash(Kpub), A] A die Adresse der Senke von T wird auf den Stack gelegt
[Sig, Kpub] OP_EQUALVERIFY es wird geprüft, ob die oberen beiden Stack-Elemente gleich sind (hier: der Hash des öffentliche Schlüssels muss mit der Adresse der Senke von T übereinstimmen), ansonsten wird das Skript vorzeitig abgebrochen
[true] OP_CHECKSIG es wird geprüft, ob die Signatur gültig ist und vom angegebenen öffentlichen Schlüssel stammt

Am Ende muss der Stack aus dem einzelnen Element [true] bestehen, dann ist die Validierung erfolgreich abgeschlossen.

Es gibt noch zahlreiche andere Operationen, die im Skript benutzt werden können. Mit dem Code OP_CHECKLOCKTIMEVERIFY kann zum Beispiel eine Sperrfrist vorgegeben werden, so dass über einen Betrag erst nach einer bestimmten Zeit verfügt werden kann. Darüber lassen sich z.B. Treuhand-artige Anwendungsfälle implementieren, bei denen Mittel bis zu einem bestimmten Zeitpunkt von einer Person abgerufen werden müssen, bevor sie dann verfallen und zurückfließen.

Adresstypen

Kommen wir nun zurück auf die Arten von Adressen, die Bitcoin unterstützt. Seit BIP 13 und BIP 173 gibt es derer drei:

Die beiden praktisch relevanten Arten sind P2PKH und P2SH. P2PKH entspricht dem klassischen Adresstyp, der aus einem Schlüsselpaar erzeugt wird. Diese Adressen beginnen mit einer 1. Im Regelfall wird als Skript das obige Beispiel benutzt, um eine P2PKH-Adresse als Senke zu verwenden.

BIP 13 hat den Typ P2SH standardisiert, deren Adressen mit einer 3 beginnen.

Bei beiden Typen werden die Adressen auf ähnliche Art gebildet:

address = RIPEMD160(SHA256(key))

SHA256 und RIPEMD160 sind kryptografische Hash-Funktionen. Bei P2PKH-Adressen ist key der öffentliche Schlüssel. Bemerkenswert ist, dass der öffentliche Schlüssel trotz seines Namens geheim ist, da er gehasht wird, um die Adresse zu bilden. Im obigen Beispiel gibt Transaktion T lediglich diesen Hash im Senken-Skript an. Erst Transaktion U muss den gesamten öffentlichen Schlüssel für A offenlegen, in dem dieser in den Eingabewerten des Skripts vorkommt.

Wichtig ist, dass die Transaktion T festlegen kann, wie die Adresse A die erhaltenen Bitcoins verwenden kann. Mit P2SH-Adressen hingegen kann man diese Entscheidung teilweise der Transaktion überlassen, die T als Quelle definiert hat.

Pay-to-script-hash-Transaktionen

Bei P2SH-Adressen ist der key, aus dem die Adresse gebildet wird, selbst ein Skript, welches zusätzlich zum Senken-Skript ausgeführt wird. Nehmen wir mal an, eine Transaktion T' sendet Bitcoin an eine P2SH-Adresse ScrHash. Damit U' diese Bitcoin weiter verteilen kann, müssen zwei Bedingungen erfüllt sein:

  1. U' muss ein Skript Scr definieren, dessen Hash genau der Senke in T' entspricht (d.h. ScrHash = hash(Scr))
  2. das Skript muss ohne Fehler ablaufen

Glücklicherweise konnte diese Art von Adressen implementiert werden, ohne das Transaktionsformat zu ändern. Eine Transaktion mit P2SH-Senke benutzt folgendes Skript:

OP_HASH160 <ScrHash> OP_EQUAL

U' gibt als Eingabedaten eine Liste von Strings und zusätzlich ein serialisiertes Skript an. Die Validierung von U' läuft in zwei Runden ab. Zunächst folgt eine Validierung, die genau so abläuft wie die von U:

Stack–Zustand Operation Erklärung
[Data₁, Data₂, …, Scr] (keine) Startzustand
[Data₁, Data₂, …, hash(Scr)] OP_HASH160 oberstes Element wird gehasht
[Data₁, Data₂, …, hash(Scr), ScrHash] ScrHash die Adresse der Senke von T' wird auf den Stack gelegt
[Data₁, Data₂, …, true] OP_EQUAL es wird geprüft, ob die oberen beiden Stackelemente gleich sind

Damit ist die erste Validierungsrunde erfolgreich. Für die zweite Runde wird das Skript Scr ausgeführt:

Stack–Zustand Operation Erklärung
[Data₁, Data₂, …] (keine) Startzustand

Ab diesem Zustand können beliebige Operationen ausgeführt werden.

Interessanterweise lassen sich mit P2SH-Transaktionen gewöhnliche P2PKH-Transaktionen simulieren. Dazu genügt es, wenn Scr die üblichen Instruktionen enthält:

OP_DUP OP_HASH160 <A> OP_EQUALVERIFY OP_CHECKSIG

Insgesamt muss also U' folgende Eingabedaten angeben:

<Sig> <Kpub> <Scr>

Die zweite Validierungsrunde läuft dann exakt wie die Validierung von T ab.

Weiter oben schrieb ich:

Wichtig ist, dass die Transaktion T festlegen kann, wie die Adresse A die erhaltenen Bitcoins verwenden kann. Mit P2SH-Adressen hingegen kann man diese Entscheidung teilweise der Transaktion überlassen, die T als Quelle definiert hat.

Streng genommen ist das nicht korrekt, da die P2SH-Adresse bereits festlegt, wie die Verwendung der Bitcoins zu erfolgen hat. Der Hauptvorteil ist, dass man aus einem Skript einfach eine Adresse bilden kann. Eine eingehende Transaktion kann dann diese Adresse spezifizieren; man ist nicht dazu gezwungen, komplizierte Skripte vorzugeben. In der Ausdrucksstärke unterscheiden sich beide Verfahren nicht: das eine kann das andere simulieren.

Einer Erhebung vom Februar 2016 zufolge kommen Senken mit P2PKH-Adressen ungefähr 37 Mal so oft vor wie solche mit P2SH-Adressen.

BIP 12

Ursprünglich war in BIP 12 ein anderes Verfahren vorgeschlagen worden. Mit der jetzigen Implementierung muss ein Bitcoin-Client P2SH-Transaktionen anhand der speziellen Adresse erkennen. BIP 12 hätte eine neue Instruktion OP_EVAL eingeführt, die wie folgt zu benutzen gewesen wäre:

OP_DUP OP_HASH160 <ScrHash> OP_EQUALVERIFY OP_EVAL

Die letzte Instruktion sorgt dafür, dass das Skript ausgeführt wird. Wie der Name bereits suggeriert, ist das ähnlich zu eval in JavaScript, mit dem man beliebigen Code ausführen kann. Mit OP_EVAL wäre die Komplexität des Skript-Interpreters deutlich gestiegen, denn damit wäre theoretisch unbeschränkte Rekursion möglich geworden. Deswegen wurde es durch BIP 16 ersetzt. Ein User auf dem Bitcoin-Stack-Exchange-Forum erklärt (Hervorhebung durch mich):

The biggest drawback of OP_EVAL (BIP 12) was making the scripting system used for transactions turing complete, thus blowing any attempt at static analysis out of the water. This was regarded as a very serious issue and BIP 12 has been pretty much shot and buried.

Für Bitcoin wurde entschieden, der Komplexität der Skriptsprache enge Grenzen zu setzen.

  1. Die Signatur kann nicht aus der gesamten Transaktion gebildet werden, da die Signatur selbst Teil der Transaktion ist. Das tatsächliche Verfahren ist kompliziert und unterliegt Änderungen im Protokoll.  ↩

Fazit

Bitcoin ist eine populäre Kryptowährung, deren Innereien komplex und schwierig zu verstehen sind. Gängige Analogien wie Banken und Konten funktionieren für Bitcoin nicht; stattdessen stehen die Transaktionen mit ihren Quellen und Senken im Vordergrund. Die eingebaute Skriptsprache ist bewusst klein gehalten, um den Aufwand für die Ausführung von Skripten zu begrenzen. Ein System, welches sich in diesen Gesichtspunkten anders entschieden hat, wird im nächsten Artikel der Serie beschrieben.

TAGS