Inhaltsverzeichnis
Du bist hier, weil du, wie ich, vom Aufstieg der Kryptowährungen begeistert bist. Aber du möchtest wissen, wie eine Blockchain denn genau funktioniert.
Wichtig ist, dass man die grundlegende Technologie dahinter richtig versteht.
Aber Blockchains zu verstehen, ist nicht einfach oder zumindest war es das zu Beginn für mich.
Ich mag es sehr Neues zu lernen. Und wenn es technisch bzw. logisch ist, zwingt es mich, mit dem Thema auf Sourcecode-Ebene zu befassen. Und genau so habe ich erst richtig verstanden, wie eine Blockchain richtig funktioniert und wo die Vor- als auch Nachteile liegen. Im Sourcecode gibt es kein Marketing-Bla Bla, hier geht es wie bei der Mathematik um Fakten.
Deshalb glaube ich wenn du das gleiche tust und dir eine eigene Blockchain entwickelst, hast du am Ende eine funktionierende Blockchain mit einem soliden Verständnis dieser bahnbrechenden Funktionsweise.
Bevor du anfängst …
Denke daran, dass eine Blockchain nichts anderes ist, als eine unveränderliche, sequenzielle Kette von Datensätzen, die Blöcke genannt werden. Sie können tatsächliche Transaktionen, Dateien, Verknüpfungen, Datei-Hashes oder beliebige Daten enthalten. Aber das allerwichtigste ist, dass sie mit Hashes miteinander verkettet sind.
Wenn du dir nicht sicher bist, was ein Hash ist, hier ist eine Erklärung.
An wen richtet sich dieser Leitfaden? Du solltest grundlegendes Programmier Verständnis mitbringen und sowie ein gewisses Verständnis dafür haben, wie HTTP-Anfragen funktionieren, da wir mit unserer Blockchain über HTTP sprechen werden.
Was brauche ich?
Überprüfe, dass Python 3.6+ (zusammen mit pip) installiert ist.
Du must auch Flask und die „wonderful Requests library“ installieren:
pip install Flask==0.12.2 requests==2.18.
OK, jetzt benötigst du noch einen HTTP-Client wie bspw.: cURL oder Postman.
Schritt 1: Erstellen einer Blockchain
Öffnen dazu einen bevorzugten Texteditor oder IDE. Ich nutze persönlich auf Windows Notepad++. Danach erstelle eine neue Datei Namens blockchain.py.
Darstellung einer Blockchain
Wir erstellen eine Blockchain-Klasse, deren Konstruktor eine anfängliche leere Liste erstellt (um unsere Blockchain zu speichern) und eine weitere, um Transaktionen zu speichern.
Hier ist der Entwurf für unsere Klasse:
class Blockchain(object):
def __init__(self):
self.chain = []
self.current_transactions = []def new_block(self):
# Erstellt einen neuen Block und fügt ihn der Kette hinzu
passdef new_transaction(self):
# Fügt der Liste der Transaktionen eine neue Transaktion hinzu
pass@staticmethod
def hash(block):
# Hascht einen Block
pass@property
def last_block(self):
# Gibt den letzten Block in der Kette zurück
pass
Jetzt haben wir eine Blockchain-Klasse, welche verantwortlich ist für die Verwaltung der Kette von Blöcken. Es speichert Transaktionen und verfügt über einige Hilfsmethoden, wie das Hinzufügen neuer Blöcke zur Kette. Nun, fangen wir an, einige Methoden auszuarbeiten.
Wie sieht ein Block aus?
Jeder Block hat einen sogenannten Index, einen Zeitstempel (Timestamp in Unix-Zeit), eine Liste von Transaktionen, einen Beweis (Proof, mehr dazu später) und den Hash des vorherigen Blocks.
Hier ist ein Beispiel, wie ein einzelner Block aussieht:
block = {
‚index‘: 1,
‚timestamp‘: 17060526454.431575,
‚transactions‘: [
{
’sender‘: „fdcf4254fc02e5e41e545599f0be4f9f65e8be431ebc1fd301a96ea88dd0d5d6“,
‚recipient‘: „d2fd12aface26a8af00f16e6aa736a0bc1ba6f41acb31ed980c2fc4d47017c2b“,
‚amount‘: 10,
}
],
‚proof‘: 197656a2604c45400a8a7c60f3f2daa8359a68c6,
‚previous_hash‘: „d7914fe546b684688bb95f4f888a92dfc680603a75f23eb823658031fff766d9“
}
An diesem Punkt sollte die Idee einer Kette offensichtlich sein. Jeder neue Block enthält in sich den Hash des vorherigen Blocks. Dies ist entscheidend, weil es die Unveränderlichkeit von Blockchains ermöglicht.
Wenn ein Angreifer einen früheren Block in der Kette manipuliert hat, enthalten alle nachfolgenden Blöcke falsche Hashes.
Macht das Sinn? Wenn nicht, nimm bitte etwas Zeit, die Grundlagen einer Hashfunktion, eines Hashwertes, Kryptographie im Allgemeinen ein wenig genauer zu betrachten, damit du die Kernidee hinter einer Blockchain besser verstehst.
Hinzufügen von Transaktionen zu einem Block
Wir benötigen eine Möglichkeit, Transaktionen zu einem Block hinzuzufügen.
Unsere new_transaction() Methode ist genau die Richtige dafür und verantwortlich, dass Transaktionen zu einem Block hinzugefügt werden:
class Blockchain(object):
[…code…]
def new_transaction(self, sender, recipient, amount):
„““
Erstellt eine neue Transaktion, um in den nächsten erstellten Block zu gelangen:param absender: <str> Adresse des Absenders in String
:param empfaenger: <str> Adresse des Empfängers in String
:param anzahl: <int> Anzahl in Integer
:return: <int> Der Index des Blocks, der diese Transaktion enthält in Integer
„““self.current_transactions.append({
‚absender‘: absender,
‚empfaenger‘: empfaenger,
‚anzahl‘: anzahl,
})return self.last_block[‚index‘] + 1
Nachdem die Methode new_transaction() der Liste eine Transaktion hinzugefügt hat, gibt es den Index des Blocks zurück, zu dem die Transaktion hinzugefügt wird, der nächste, Sprich der nächste Block der von den Minern generiert wird. Diese Information ist später für den Benutzer nützlich, der die Transaktion übermittelt.
Neue Blöcke erstellen
Wenn unsere Blockchain-Klasse instanziiert wird, müssen wir sie mit einem Genesis-Block versehen – einem Block ohne Vorgänger – also der allererste in der Kette. Wir müssen auch einen „Beweis“ (Proof) zu unserem Genese-Block hinzufügen, der das Ergebnis des Mining (oder des Arbeitsbeweises Proof of Work) ist. Wir werden später mehr über Mining sprechen.
Zusätzlich zum Erstellen des Genesis-Blocks in unserem Konstruktor werden wir auch die Methoden für new_block(), new_transaction() und hash() ergänzen:
import hashlib
import json
from time import timeclass Blockchain(object):
def __init__(self):
self.current_transactions = []
self.chain = []# Erstelle den genesis block
self.new_block(previous_hash=1, proof=100)def new_block(self, proof, previous_hash=None):
„““
Erstelle einen neuen Block in der Blockchain:param beweis: Der Beweis auch Proof genannt, der durch den Proof of Work-Algorithmus gegeben ist
:param vorheriger_hashwert: (Optional) der Hashwert des vorherigen Block
:return: Neuer Block
„““block = {
‚index‘: laenge(self.chain) + 1,
‚timestamp‘: time(),
‚transactions‘: self.current_transactions,
‚beweis‘: beweis,
‚vorheriger_hashwert‘: vorheriger_hashwert oder self.hash(self.chain[-1]),
}# Setzt die aktuelle Liste der Transaktionen zurück
self.current_transactions = []self.chain.append(block)
return blockdef new_transaction(self, sender, recipient, amount):
„““
Erstellt eine neue Transaktion, um in den nächsten erstellten Block zu gelangen:param absender: <str> Adresse des Absenders in String
:param empfaenger: <str> Adresse des Empfängers in String
:param anzahl: <int> Anzahl in Integer
:return: <int> Der Index des Blocks, der diese Transaktion enthält in Integer
„““
self.current_transactions.append({
‚absender‘: absender,
‚empfaenger‘: empfaenger,
‚anzahl‘: anzahl,
})return self.last_block[‚index‘] + 1
@property
def last_block(self):
return self.chain[-1]@staticmethod
def hash(block):
„““
Erstelle einen SHA-256 Hashwert des Blocks:param block: Block
:return:
„““# Wir müssen sicherstellen, dass das Verzeichnis geordnet ist, oder wir haben inkonsistente Hashes bzw. Hashwerte
block_string = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(block_string).hexdigest()
Im obigen Code Beispiel befinden sich einige Kommentare und Docstrings, um es klar zu halten. Nun sind wir fast fertig mit der Darstellung unserer Blockchain.
Ab dieser Stelle fragst du wahrscheinlich, wie neue Blöcke erstellt, generiert oder gebaut werden.
Proof of Work – PoW (Arbeitsnachweis) verstehen
Ein Proof of Work-Algorithmus (PoW) ist, wie neue Blöcke in der Blockchain erstellt oder gemint werden. Das Ziel von PoW ist es, eine Zahl zu finden, die ein Problem löst. Die Nummer muss schwer zu finden sein, aber von jedem im Netzwerk einfach – rechnerisch – verifiziert werden können. Dies ist der Kerngedanke von Proof of Work.
Wir werden uns ein sehr einfaches Beispiel ansehen, um dies zu unterstützen.
Wir entscheiden, dass der Hash einer ganzen Zahl x multipliziert mit einer anderen y in 0 enden muss. Also, Hash (x * y) = dac47ec … 0. Und für dieses vereinfachte Beispiel wollen wir x = 3 fixieren. Dies in Python implementieren:
from hashlib import sha256x = 3 y = 0 # Wir wissen nicht, was y sein wird...while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0": y += 1print(f'The solution is y = {y}')
Die Lösung ist hier y = 35. Da endet der erzeugte Hash in 0:
hash(3 * 35) = ‚1253e9373e781b7500266caa55150e08e210bc8cd8cc70d89985e3600155e860‘
In Bitcoin wird der Proof of Work-Algorithmus HashCash genannt. Und es unterscheidet sich nicht von unserem obigen Beispiel. Es ist der Algorithmus, den Miner (Validierer) lösen, um einen neuen Block zu erstellen. Im Allgemeinen hängt die Schwierigkeit von der Anzahl der Zeichen ab, nach denen in einer Zeichenfolge gesucht wird. Die Miner werden dann für ihre Lösung belohnt, indem sie bei einer Transaktion einen Coin erhalten.
Das Netzwerk kann seine Lösung leicht überprüfen.
Grundlegende Proof of Work implementieren
Nun lassen wir einen ähnlichen Algorithmus für unsere Blockchain implementieren. Unsere Regel wird dem obigen Beispiel ähnlich sein:
Suche nach einer Zahl p, die beim Hashing mit der Lösung des vorherigen Blocks einen Hash mit 4 führenden 0s erzeugt.
import hashlib import json from time import time from uuid import uuid4 class Blockchain(object): ... def proof_of_work(self, last_proof): """ Simple Proof of Work Algorithm: - Finde eine Zahl p ', so dass der Hash (pp') führende 4 Nullen enthält, wobei p der vorherige p 'ist - p ist der vorherige Beweis und p 'ist der neue Beweis :param last_proof: <int> :return: <int> """ proof = 0 while self.valid_proof(last_proof, proof) is False: proof += 1 return proof @staticmethod def valid_proof(last_proof, proof): """ Validiert den Beweis: Enthält hash (last_proof, proof) 4 führende Nullen? :param last_proof: <int> Previous Proof :param proof: <int> Current Proof :return: <bool> True if correct, False if not. """ guess = f'{last_proof}{proof}'.encode() guess_hash = hashlib.sha256(guess).hexdigest() return guess_hash[:4] == "0000"
Um die Schwierigkeit des Algorithmus anzupassen, könnten wir die Anzahl der führenden Nullen modifizieren. Aber 4 ist ausreichend. Du wirst feststellen, dass das Hinzufügung einer einzelnen führenden Null einen großen Unterschied bei der Laufzeit aus macht, die für die Suche nach einer Lösung erforderlich ist.
Unsere Klasse ist fast vollständig und wir sind bereit, mit der Interaktion mit HTTP-Anfragen zu beginnen.