
Hallo, liebe Habr-User! In diesem Artikel geht es um Web 3.0 - das dezentrale Internet. Web 3.0 führt das Konzept der Dezentralisierung als Grundlage des modernen Internets ein. Viele Computersysteme und Netzwerke benötigen Sicherheits- und Dezentralisierungsfunktionen, um ihre Anforderungen zu erfüllen. Eine verteilte Registrierung mit Blockchain-Technologie bietet effiziente Lösungen für die Dezentralisierung.
Blockchain ist eine verteilte Registrierung. Sie können sich das als eine riesige Datenbank vorstellen, die für immer lebt und sich im Laufe der Zeit nie ändert. Die Blockchain bildet die Basis für dezentrale Webanwendungen und -dienste.
Die Blockchain ist jedoch mehr als nur eine Datenbank. Es dient dazu, die Sicherheit und das Vertrauen zwischen Netzwerkmitgliedern zu erhöhen und Online-Geschäftstransaktionen zu verbessern.
Der byzantinische Konsens erhöht die Netzwerkzuverlässigkeit und löst das Problem der Konsistenz.
Die von DLT bereitgestellte Skalierbarkeit ändert vorhandene Geschäftsnetzwerke.
Blockchain bietet neue, sehr wichtige Vorteile:
- Verhindert kostspielige Fehler.
- Gewährleistet transparente Transaktionen.
- Digitalisiert echte Waren.
- Erzwingt intelligente Verträge.
- Erhöht die Geschwindigkeit und Sicherheit von Zahlungen.
Wir haben ein spezielles PoE entwickelt, um kryptografische Protokolle zu untersuchen und vorhandene DLT- und Blockchain-Lösungen zu verbessern.
Den meisten öffentlichen Registrierungssystemen fehlt die Eigenschaft der Skalierbarkeit, wodurch ihr Durchsatz eher gering ist. Zum Beispiel verarbeitet Ethereum nur ~ 20 tx / s.
Viele Lösungen wurden entwickelt, um die Skalierbarkeit zu erhöhen und gleichzeitig die Dezentralisierung aufrechtzuerhalten. Es können jedoch nur zwei von drei Vorteilen - Skalierbarkeit, Sicherheit und Dezentralisierung - gleichzeitig erzielt werden.
Die Verwendung von Seitenketten bietet eine der effektivsten Lösungen.
Das Plasmakonzept
Das Plasma-Konzept läuft auf die Idee hinaus, dass eine Wurzelkette eine kleine Anzahl von Commits von untergeordneten Ketten verarbeitet und somit als sicherste und letzte Schicht zum Speichern aller Zwischenzustände fungiert. Jede untergeordnete Kette arbeitet als eigene Blockchain mit einem eigenen Konsensalgorithmus, es gibt jedoch einige wichtige Einschränkungen.
- Intelligente Verträge werden in einer Stammkette erstellt und dienen als Prüfpunkte für untergeordnete Ketten innerhalb der Stammkette.
- Eine untergeordnete Kette wird erstellt und fungiert als eigene Blockchain mit eigenem Konsens. Alle Staaten in der Kinderkette sind durch Betrugsnachweise geschützt, die sicherstellen, dass alle Übergänge zwischen Staaten gültig sind und ein Widerrufsprotokoll anwenden.
- Intelligente Verträge, die für DApp oder die Anwendungslogik der untergeordneten Kette spezifisch sind, können in der untergeordneten Kette bereitgestellt werden.
- Geldmittel können von der Wurzelkette auf die untergeordnete Kette übertragen werden.
Validatoren erhalten wirtschaftliche Anreize, ehrlich zu handeln und Verpflichtungen an die Wurzelkette zu senden - die letzte Transaktionsabwicklungsschicht.
Daher müssen DApp-Benutzer, die in der untergeordneten Kette arbeiten, überhaupt nicht mit der Stammkette interagieren. Außerdem können sie ihr Geld jederzeit in die Wurzelkette legen, selbst wenn die Kinderkette gehackt wird. Diese Ausgänge aus der Kinderkette ermöglichen es Benutzern, ihre Gelder mit Merkle-Beweisen sicher zu speichern, was den Besitz eines bestimmten Geldbetrags bestätigt.
Die Hauptvorteile von Plasma hängen mit seiner Fähigkeit zusammen, Berechnungen, die die Hauptkette überlasten, erheblich zu vereinfachen. Darüber hinaus kann die Ethereum-Blockchain umfangreichere und parallelere Datensätze verarbeiten. Die aus der Wurzelkette entfernte Zeit wird auch auf Ethereum-Knoten übertragen, die geringere Verarbeitungs- und Speicheranforderungen haben.
Plasma Cash weist Online-Token eindeutige Seriennummern zu. Zu den Vorteilen dieses Schemas gehören keine Notwendigkeit für Bestätigungen, eine einfachere Unterstützung für alle Arten von Token (einschließlich nicht fungibler Token) und eine Abschwächung gegen Massenausgänge aus einer Kinderkette.
Das Konzept der „Massenausgänge“ aus einer Kinderkette ist ein Problem, mit dem Plasma konfrontiert ist. In diesem Szenario können koordinierte gleichzeitige Abhebungen aus einer untergeordneten Kette möglicherweise zu einer unzureichenden Rechenleistung führen, um alle Mittel abzuheben. Infolgedessen können Benutzer Geld verlieren.
Optionen zur Implementierung von Plasma

Basic Plasma bietet viele Implementierungsoptionen.
Die Hauptunterschiede beziehen sich auf:
- Archivierung von Informationen zu staatlichen Speicher- und Präsentationsmethoden;
- Token-Typen (teilbar, unteilbar);
- Transaktionssicherheit;
- Konsensalgorithmus-Typ.
Die Hauptvarianten von Plasma umfassen:
- UTXO-basiertes Plasma - Jede Transaktion besteht aus Ein- und Ausgängen. Eine Transaktion kann durchgeführt und ausgegeben werden. Die Liste der nicht ausgegebenen Transaktionen ist der Status einer untergeordneten Kette.
- Kontobasiertes Plasma - Diese Struktur enthält die Reflexion und den Kontostand jedes Kontos. Es wird in Ethereum verwendet, da es zwei Arten von Konten geben kann: ein Benutzerkonto und ein Smart-Vertragskonto. Einfachheit ist ein wichtiger Vorteil von kontobasiertem Plasma. Gleichzeitig ist die mangelnde Skalierbarkeit ein Nachteil. Eine spezielle Eigenschaft, "nonce", wird verwendet, um die zweimalige Ausführung einer Transaktion zu verhindern.
Um die in der Plasma Cash-Blockchain verwendeten Datenstrukturen und die Funktionsweise von Verpflichtungen zu verstehen, muss das Konzept von Merkle Tree geklärt werden.
Merkle Trees und ihre Verwendung im Plasma
Merkle Tree ist eine äußerst wichtige Datenstruktur in der Blockchain-Welt. Es ermöglicht uns, einen bestimmten Datensatz zu erfassen und die Daten auszublenden, jedoch zu beweisen, dass einige Informationen im Satz enthalten waren. Wenn wir beispielsweise zehn Zahlen haben, können wir einen Beweis für diese Zahlen erstellen und dann beweisen, dass eine bestimmte Zahl in diesem Satz enthalten ist. Dieser Beweis hätte eine kleine konstante Größe, was die Veröffentlichung in Ethereum kostengünstig macht.
Sie können dieses Prinzip für eine Reihe von Transaktionen verwenden und nachweisen, dass sich eine bestimmte Transaktion in dieser Reihe befindet. Genau das macht ein Bediener. Jeder Block besteht aus einem Transaktionssatz, der sich in einen Merkle-Baum verwandelt. Die Wurzel dieses Baumes ist ein Beweis, der zusammen mit jedem Plasmablock in Ethereum veröffentlicht wird.
Benutzer sollten in der Lage sein, ihr Geld aus der Plasmakette abzuheben. Zu diesem Zweck senden sie eine "Exit" -Transaktion an Ethereum.
Plasma Cash verwendet einen speziellen Merkle-Baum, der die Validierung eines ganzen Blocks überflüssig macht. Es reicht aus, nur die Zweige zu validieren, die dem Token des Benutzers entsprechen.
Um ein Token zu übertragen, müssen Sie seinen Verlauf analysieren und nur die Token scannen, die ein bestimmter Benutzer benötigt. Bei der Übertragung eines Tokens sendet der Benutzer einfach den gesamten Verlauf an einen anderen Benutzer, der dann den gesamten Verlauf authentifizieren und vor allem sehr schnell ausführen kann.

Plasma Cash-Datenstrukturen für die Speicherung von Status und Verlauf
Es ist ratsam, nur ausgewählte Merkle-Bäume zu verwenden, da für eine Transaktion in einem Block Einschluss- und Nichteinschlussnachweise erforderlich sind. Zum Beispiel:
- Spärlicher Merkle-Baum
- Patricia Baum
Wir haben für unseren Kunden eigene Sparse Merkle Tree- und Patricia Tree-Implementierungen entwickelt.
Ein spärlicher Merkle-Baum ähnelt einem Standard-Merkle-Baum, außer dass seine Daten indiziert werden und jeder Datenpunkt auf einem Blatt platziert wird, das dem Index dieses Datenpunkts entspricht.
Angenommen, wir haben einen vierblättrigen Merkle-Baum. Füllen wir diesen Baum zur Demonstration mit den Buchstaben A und D. Der Buchstabe A ist der erste Buchstabe des Alphabets, daher platzieren wir ihn auf dem ersten Blatt. In ähnlicher Weise werden wir D auf das vierte Blatt setzen.
Was passiert also beim zweiten und dritten Blatt? Sie sollten leer gelassen werden. Genauer gesagt wird anstelle eines Buchstabens ein spezieller Wert (z. B. Null) verwendet.
Der Baum sieht schließlich so aus:

Der Einschlussnachweis funktioniert genauso wie bei einem normalen Merkle-Baum. Was passiert, wenn wir beweisen wollen, dass C nicht Teil dieses Merkle-Baums ist? Grundstufe! Wir wissen, dass wenn C ein Teil eines Baumes ist, es sich auf dem dritten Blatt befindet. Wenn C kein Teil des Baumes ist, sollte das dritte Blatt Null sein.
Alles, was benötigt wird, ist ein Standard-Merkle-Einschlussnachweis, der zeigt, dass das dritte Blatt Null ist.
Das Beste an einem Sparse Merkle Tree ist, dass er Repositories für Schlüsselwerte im Merkle Tree bereitstellt!
Ein Teil des PoE-Protokollcodes erstellt einen Sparse Merkle Tree:
class SparseTree { //... buildTree() { if (Object.keys(this.leaves).length > 0) { this.levels = [] this.levels.unshift(this.leaves) for (let level = 0; level < this.depth; level++) { let currentLevel = this.levels[0] let nextLevel = {} Object.keys(currentLevel).forEach((leafKey) => { let leafHash = currentLevel[leafKey] let isEvenLeaf = this.isEvenLeaf(leafKey) let parentLeafKey = leafKey.slice(0, -1) let neighborLeafKey = parentLeafKey + (isEvenLeaf ? '1' : '0') let neighborLeafHash = currentLevel[neighborLeafKey] if (!neighborLeafHash) { neighborLeafHash = this.defaultHashes[level] } if (!nextLevel[parentLeafKey]) { let parentLeafHash = isEvenLeaf ? ethUtil.sha3(Buffer.concat([leafHash, neighborLeafHash])) : ethUtil.sha3(Buffer.concat([neighborLeafHash, leafHash])) if (level == this.depth - 1) { nextLevel['merkleRoot'] = parentLeafHash } else { nextLevel[parentLeafKey] = parentLeafHash } } }) this.levels.unshift(nextLevel) } } } }
Dieser Code ist ziemlich trivial. Wir haben ein Schlüsselwert-Repository mit einem Einschluss- / Nichteinschlussnachweis.
In jeder Iteration wird eine bestimmte Ebene eines endgültigen Baums gefüllt, beginnend mit der letzten. Abhängig davon, ob der Schlüssel des aktuellen Blattes gerade oder ungerade ist, nehmen wir zwei benachbarte Blätter und zählen den Hash des aktuellen Levels. Wenn wir das Ende erreichen, würden wir einen einzelnen merkleRoot aufschreiben - einen gemeinsamen Hash.
Sie müssen verstehen, dass dieser Baum mit anfänglich leeren Werten gefüllt ist. Wenn wir eine große Menge an Token-IDs speichern würden, hätten wir eine große Baumgröße und es wäre lang!
Es gibt viele Abhilfemaßnahmen für diese Nichtoptimierung, aber wir haben beschlossen, diesen Baum in einen Patricia-Baum zu ändern.
Ein Patricia Tree ist eine Kombination aus Radix Tree und Merkle Tree.
Ein Radix Tree-Datenschlüssel speichert den Pfad zu den Daten selbst, wodurch wir eine optimierte Datenstruktur für den Speicher erstellen können.

Hier ist eine Implementierung, die für unseren Kunden entwickelt wurde:
buildNode(childNodes, key = '', level = 0) { let node = {key} this.iterations++ if (childNodes.length == 1) { let nodeKey = level == 0 ? childNodes[0].key : childNodes[0].key.slice(level - 1) node.key = nodeKey let nodeHashes = Buffer.concat([Buffer.from(ethUtil.sha3(nodeKey)), childNodes[0].hash]) node.hash = ethUtil.sha3(nodeHashes) return node } let leftChilds = [] let rightChilds = [] childNodes.forEach((node) => { if (node.key[level] == '1') { rightChilds.push(node) } else { leftChilds.push(node) } }) if (leftChilds.length && rightChilds.length) { node.leftChild = this.buildNode(leftChilds, '0', level + 1) node.rightChild = this.buildNode(rightChilds, '1', level + 1) let nodeHashes = Buffer.concat([Buffer.from(ethUtil.sha3(node.key)), node.leftChild.hash, node.rightChild.hash]) node.hash = ethUtil.sha3(nodeHashes) } else if (leftChilds.length && !rightChilds.length) { node = this.buildNode(leftChilds, key + '0', level + 1) } else if (!leftChilds.length && rightChilds.length) { node = this.buildNode(rightChilds, key + '1', level + 1) } else if (!leftChilds.length && !rightChilds.length) { throw new Error('invalid tree') } return node }
Wir sind rekursiv umgezogen und haben die separaten linken und rechten Teilbäume erstellt. In diesem Baum wurde ein Schlüssel als Pfad erstellt.
Diese Lösung ist noch trivialer. Es ist gut optimiert und arbeitet schneller. Tatsächlich kann ein Patricia Tree noch weiter optimiert werden, indem neue Knotentypen eingeführt werden - Erweiterungsknoten, Verzweigungsknoten usw., wie im Ethereum-Protokoll. Die aktuelle Implementierung erfüllt jedoch alle unsere Anforderungen - wir haben eine schnelle und speicheroptimierte Datenstruktur.
Durch die Implementierung dieser Datenstrukturen im Projekt unseres Kunden haben wir die Skalierung von Plasma Cash ermöglicht. Auf diese Weise können wir den Verlauf eines Tokens und das Einschließen / Nichteinschließen des Tokens in einen Baum überprüfen, wodurch die Validierung von Blöcken und der untergeordneten Plasmakette erheblich beschleunigt wird.
Links:
- Weißbuchplasma
- Git Hub
- Anwendungsfälle und Architekturbeschreibung
- Lightning-Netzwerkpapier