Lightning Inter-Node-Protokoll: Ein Primer

Das ursprüngliche Protokoll, das ich für die Kommunikation zweier Blitzknoten geschrieben habe, war einfach: Um eine Änderung vorzunehmen, haben Sie sie vorgeschlagen und die Unterschrift für ihre Verpflichtungstransaktion gesendet. Sie haben mit dem Widerrufsgeheimnis für die vorherige und einer Unterschrift für Sie geantwortet. Dann haben Sie es beendet, indem Sie eine Unterschrift für sie bereitgestellt haben. Wenn beide Seiten gleichzeitig Änderungen vorgeschlagen haben, hat sich eine zurückgezogen.

Leider war es zu einfach: J o seph Poon wollte etwas viel effizienteres. Das gut zu machen war eine längere Reise als ich wollte, aber es lohnt sich zu schreiben, wo wir hingekommen sind, da dies sicherlich viel Verwirrung stiften wird.

Es gibt zwei Möglichkeiten, wie wir es effizienter gestalten können: Batching bedeutet, dass wir mehr als ein Update gleichzeitig senden können, und Desynchronisation bedeutet, dass beide Seiten unabhängig sind. Die Regeln dafür sind nicht sehr kompliziert, aber das System insgesamt kann sehr schwer zu befolgen sein. So sehr, dass ich am Ende einen Protokollsimulator geschrieben habe, um sicherzugehen, dass er funktioniert.

Jeder Knoten verfolgt zwei Commitment-Transaktionen. seine lokale (die in die Blockchain verschoben wird, wenn etwas schief geht) und die entfernte (die möglicherweise in der Blockchain angezeigt wird, wenn die andere Seite sie freigibt). Sowohl für die lokalen als auch für die Remote-Verpflichtungen werden zwei Arten von Änderungen verfolgt: nicht bestätigte Änderungen und bestätigte Änderungen.

Knoten A überträgt eine Reihe von Aktualisierungen auf die Remote-Festschreibung, z. B. “Diese neue HTLC anbieten”, “Ich löse Ihre vorhandene HTLC ein” oder “Ihre vorhandene HTLC konnte nicht weitergeleitet werden”. Irgendwann sperrt A sie, indem es eine Festschreibungsnachricht mit ihrer Signatur für die neue Remote-Festschreibungstransaktion mit diesen Änderungen sendet. Knoten B überprüft die Signatur und gibt das Widerrufsvorbild für seine vorherige Verpflichtungstransaktion zurück. Er verspricht, es niemals zu verwenden.

Knoten B hat also eine signierte Commitment-Transaktion mit den Änderungen, Knoten A jedoch nicht. Knoten B könnte genau dieselben Aktualisierungen an A zurücksenden, gefolgt von einem Festschreiben, aber die Sperrnachricht dient demselben Zweck: A weiß, dass B alle diese Aktualisierungen gesehen hat, sodass er diese Änderungen nun in seine eigene Festschreibungstransaktion einreihen kann . Es benötigt jedoch noch die Unterschrift von B.

B sendet nun eine eigene Aktualisierungsnachricht und signiert die Verpflichtungstransaktion von A mit den vorgenommenen Änderungen. A überprüft die Signatur und sendet das vorherige Widerrufsvorbild, um die alte Verpflichtungstransaktion zu veralten. Beide Seiten sind synchron.

Hier ist ein automatisch generiertes Diagramm von A, das HTLC # 1 hinzufügt:

Der allgemeine Fall

Um den Fall zu behandeln, in dem B gleichzeitig Vorschläge macht, müssen wir etwas formeller werden. Denken Sie daran, dass jeder Knoten Vorschläge für die Remote-Seite macht. Diese Änderungen werden erst lokal angewendet, wenn sie bestätigt wurden.

Die beiden Listen stellen sicher, dass jede Änderung zuerst auf den Empfängerknoten und dann auf den Antragstellerknoten angewendet wird. Es gibt natürlich verschiedene Möglichkeiten für eine Implementierung, um dies zu optimieren.

Hier ist ein Fall, in dem beide gleichzeitig eine HTLC anbieten und dann einen Flug hin und her ausführen, bis beide wieder synchron sind:

Gebühren

Gebührenverhandlungen sind etwas seltsam: Ich habe kein wirkliches Interesse daran, welche Gebühren die Verpflichtung des anderen Knotens zahlt (im Rahmen des Zumutbaren), aber ich muss die Gebühren für meine Verpflichtung wettbewerbsfähig halten, falls ich die Transaktion in die Blockchain verschieben muss . Im Gegensatz zu HTLCs sind sie also nicht symmetrisch.

Wir passen “Gebührenänderungs” -Nachrichten in dasselbe Yours-Then-Mine-Modell ein, indem wir deren Gebührenänderungen ignorieren, wenn wir Änderungen an unserer eigenen Verpflichtungstransaktion vornehmen (technisch gesehen wenden wir nur Gebührenänderungen von bestätigt an einstellen). Wir senden also wie jede andere Änderung eine Gebührenänderung, senden das Commit und wenn die andere Seite ein Commit durchführt, passen wir schließlich die Gebühr für unsere eigene Commit-Transaktion an. Glücklicherweise sollten Gebührenänderungen nicht sehr häufig sein:

Autsch mein Gehirn!

Bei diesem Entwurf gab es mehrere falsche Wendungen. Es durchlief mehrere Iterationen mit expliziten Bestätigungsnummern, bevor ich davon überzeugt war, dass sie unnötig waren. Meine Implementierung hatte einen Fehler beim Überkreuzen von Commit-Nachrichten, der so verwirrend war, dass ich zu den ersten Prinzipien zurückkehren musste. Dies hat auch zu Verwirrung auf der Mailingliste geführt.

Es wäre leicht, Neugeborenen und Schlafmangel die Schuld zu geben, aber die Wahrheit ist, dass Protokolle einfach genug sein müssen, um schlecht implementiert zu werden: Sie werden es trotzdem sein!

Die Implementierung in meinem Prototyp ist die letzte Ausgabe der Version 0.3, die dank Braydon Fuller bereits einen Codenamen hat…