FlowCards: Ein deklaratives Framework zur Entwicklung von Ergo dApps
29. April 2020

Dank an Robert Kornacki für die Überarbeitung des Entwurfs.
Einführung
ErgoScript ist die Smart-Contract-Sprache, die von der Ergo-Blockchain verwendet wird. Während sie eine prägnante Syntax hat, die von Scala/Kotlin übernommen wurde, kann sie anfangs verwirrend erscheinen, da ErgoScript konzeptionell ganz anders ist als die konventionellen Sprachen, die wir alle kennen und lieben. Dies liegt daran, dass Ergo eine UTXO-basierte Blockchain ist, während Smart Contracts traditionell mit kontobasierten Systemen wie Ethereum verbunden sind. Das Transaktionsmodell von Ergo hat jedoch viele Vorteile gegenüber dem kontobasierten Modell, und mit dem richtigen Ansatz kann es sogar erheblich einfacher sein, Ergo-Verträge zu entwickeln, als Solidity-Code zu schreiben und zu debuggen.
Im Folgenden werden wir die wichtigsten Aspekte des Ergo-Vertragsmodells behandeln, die es von anderen unterscheiden:
Paradigma
Das Kontomodell von Ethereum ist imperativ. Das bedeutet, dass die typische Aufgabe, Coins von Alice an Bob zu senden, das Ändern der Salden im Speicher als eine Reihe von Operationen erfordert. Das UTXO-basierte Programmiermodell von Ergo hingegen ist deklarativ. ErgoScript-Verträge spezifizieren Bedingungen, unter denen eine Transaktion von der Blockchain akzeptiert wird (nicht Änderungen, die im Speicherzustand als Ergebnis der Vertragserfüllung vorgenommen werden).
Skalierbarkeit
Im Kontomodell von Ethereum werden sowohl Speicheränderungen als auch Gültigkeitsprüfungen on-chain während der Codeausführung durchgeführt. Im Gegensatz dazu werden Ergo-Transaktionen off-chain erstellt, und es werden nur Validierungsprüfungen on-chain durchgeführt, wodurch die Anzahl der Operationen, die von jedem Knoten im Netzwerk ausgeführt werden, reduziert wird. Darüber hinaus sind aufgrund der Unveränderlichkeit des Transaktionsgraphen verschiedene Optimierungsstrategien möglich, um den Durchsatz von Transaktionen pro Sekunde im Netzwerk zu verbessern. Leichte verifizierende Knoten sind ebenfalls möglich, was die Skalierbarkeit und Zugänglichkeit des Netzwerks weiter erleichtert.
Geteilte Zustände
Das kontobasierte Modell ist auf einen gemeinsamen veränderbaren Zustand angewiesen, der bekannt dafür ist, zu komplexen Semantiken (und subtilen Millionen-Dollar-Fehlern) im Kontext der gleichzeitigen/verteilten Berechnung zu führen. Das Modell von Ergo basiert auf einem unveränderlichen Graphen von Transaktionen. Dieser Ansatz, der von Bitcoin übernommen wurde, harmoniert gut mit der gleichzeitigen und verteilten Natur von Blockchains und erleichtert vertrauenslose Clients.
Ausdruckskraft
Ethereum befürwortete die Ausführung einer turing-vollständigen Sprache auf der Blockchain. Theoretisch versprach es unbegrenztes Potenzial, jedoch kamen in der Praxis schwerwiegende Einschränkungen durch übermäßige Blockchain-Überlastung, subtile Millionen-Dollar-Fehler, Gaskosten, die die Komplexität von Verträgen einschränken, und andere Probleme ans Licht. Ergo hingegen erweitert UTXO, um Turing-Vollständigkeit zu ermöglichen, während die Komplexität der ErgoScript-Sprache selbst begrenzt wird. Die gleiche Ausdruckskraft wird auf eine andere und semantisch fundierte Weise erreicht.
Mit all den oben genannten Punkten sollte klar sein, dass es viele Vorteile für das Modell gibt, das Ergo verwendet. Im Rest dieses Artikels werde ich Ihnen das Konzept der FlowCards vorstellen - eine dApp-Entwicklerkomponente, die es ermöglicht, komplexe Ergo-Verträge auf deklarative und visuelle Weise zu entwerfen.
Von Imperativ zu Deklarativ
Im imperativen Programmiermodell von Ethereum ist eine Transaktion eine Folge von Operationen, die von der Ethereum-VM ausgeführt werden. Die folgende Solidity-Funktion implementiert einen Transfer von Tokens von sender zu receiver. Die Transaktion beginnt, wenn sender diese Funktion auf einer Instanz eines Vertrags aufruft, und endet, wenn die Funktion zurückkehrt.
// Sendet einen Betrag vorhandener Coins von jedem Aufrufer an eine Adresse
function send(address receiver, uint amount) public {
require(amount <= balances[msg.sender], "Unzureichendes Guthaben.");
balances[msg.sender] -= amount;
balances[receiver] += amount;
emit Sent(msg.sender, receiver, amount);
}
Die Funktion überprüft zunächst die Vorbedingungen, aktualisiert dann den Speicher (d.h. die Salden) und veröffentlicht schließlich die Nachbedingung als das Sent-Ereignis. Das Gas, das von der Transaktion verbraucht wird, wird dem Miner als Belohnung für die Ausführung dieser Transaktion gesendet.
Im Gegensatz zu Ethereum ist eine Transaktion in Ergo eine Datenstruktur, die eine Liste von Eingangs-Coins enthält, die sie ausgibt, und eine Liste von Ausgangs-Coins, die sie erstellt, wobei die Gesamtsalden von ERGs und Tokens (in denen Ergo Bitcoin ähnlich ist) erhalten bleiben.
Zurück zu dem obigen Beispiel, da Ergo nativ Tokens unterstützt, müssen wir für dieses spezifische Beispiel des Sendens von Tokens keinen Code in ErgoScript schreiben. Stattdessen müssen wir die 'send'-Transaktion erstellen, die in der folgenden Abbildung gezeigt wird, die den gleichen Token-Transfer, aber deklarativ beschreibt.

Das Bild beschreibt visuell die folgenden Schritte, die der Netzwerkbenutzer ausführen muss:
- Wählen Sie ungenutzte Sender-Boxen aus, die insgesamt
tB >= amountTokens undB >= txFee + minErgERGs enthalten. - Erstellen Sie eine Ausgangs-
target-Box, die durch den öffentlichen Schlüssel desreceivermitminErgERGs undamountvonTTokens geschützt ist. - Erstellen Sie eine Gebühren-Ausgabe, die durch den
minerFee-Vertrag mittxFeeERGs geschützt ist. - Erstellen Sie eine Wechsel-Ausgabe, die durch den öffentlichen Schlüssel des
sendergeschützt ist undB - minErg - txFeeERGs undtB - amountvonTTokens enthält. - Erstellen Sie eine neue Transaktion, signieren Sie sie mit dem geheimen Schlüssel des Senders und senden Sie sie an das Ergo-Netzwerk.
Wichtig zu verstehen ist, dass all diese Schritte off-chain (zum Beispiel unter Verwendung der Appkit Transaction API) von der Anwendung des Benutzers durchgeführt werden. Ergo-Netzwerkknoten müssen diesen Transaktionserstellungsprozess nicht wiederholen, sie müssen nur die bereits gebildete Transaktion validieren. ErgoScript-Verträge werden in den Eingaben der Transaktion gespeichert und überprüfen die Ausgabebedingungen. Der Knoten führt die Verträge on-chain aus, wenn die Transaktion validiert wird. Die Transaktion ist gültig, wenn alle Bedingungen erfüllt sind.
So, in Ethereum, wenn wir "Betrag von Sender an Empfänger senden", bearbeiten wir buchstäblich die Salden und aktualisieren den Speicher mit einem konkreten Satz von Befehlen. Dies geschieht on-chain und somit wird auch eine neue Transaktion als Ergebnis dieses Prozesses on-chain erstellt.
In Ergo (wie in Bitcoin) werden Transaktionen off-chain erstellt und die Netzwerknoten überprüfen sie nur. Die Auswirkungen der Transaktion auf den Blockchain-Zustand sind, dass Eingangs-Coins (oder Boxen in Ergos Sprache) entfernt und Ausgangs-Boxen zum UTXO-Set hinzugefügt werden.
Im obigen Beispiel verwenden wir keinen ErgoScript-Vertrag, sondern nehmen stattdessen an, dass eine Signaturprüfung als Vorbedingung für das Ausgeben verwendet wird. In komplexeren Anwendungsszenarien müssen wir natürlich ErgoScript verwenden, was wir als Nächstes besprechen werden.
Vom Ändern des Zustands zum Überprüfen des Kontexts
Im Beispiel der send-Funktion haben wir zunächst die Vorbedingung überprüft (require(amount <= balances[msg.sender],...)) und dann den Zustand geändert (d.h. die Salden aktualisiert balances[msg.sender] -= amount). Dies ist typisch für Ethereum-Transaktionen. Bevor wir etwas ändern, müssen wir überprüfen, ob es gültig ist, dies zu tun.
In Ergo, wie wir zuvor besprochen haben, wird der Zustand (d.h. das UTXO-Set von Boxen) implizit geändert, wenn eine gültige Transaktion in einen Block aufgenommen wird. Daher müssen wir nur die Vorbedingungen überprüfen, bevor die Transaktion zum Block hinzugefügt werden kann. Das ist es, was ErgoScript-Verträge tun.
Es ist nicht möglich, den "Zustand" in ErgoScript zu "ändern", da es sich um eine Sprache handelt, um Vorbedingungen für das Ausgeben von Coins zu überprüfen. ErgoScript ist eine rein funktionale Sprache ohne Nebenwirkungen, die auf unveränderlichen Datenwerten arbeitet. Das bedeutet, dass alle Eingaben, Ausgaben und anderen Transaktionsparameter, die in einem Skript verfügbar sind, unveränderlich sind. Dies macht ErgoScript unter anderem zu einer sehr einfachen Sprache, die leicht zu lernen und sicher zu verwenden ist. Ähnlich wie bei Bitcoin enthält jede Eingangsbox ein Skript, das den Wert true zurückgeben sollte, um 1) das Ausgeben der Box zu erlauben (d.h. das Entfernen aus dem UTXO-Set) und 2) die Transaktion zum Block hinzuzufügen.
Wenn wir genau sein wollen, ist es daher (streng genommen) falsch, ErgoScript als die Sprache der Ergo-Verträge zu betrachten, da es die Sprache der Propositionen (logische Prädikate, Formeln usw.) ist, die Boxen vor "illegalem" Ausgeben schützen. Im Gegensatz zu Bitcoin ist in Ergo die gesamte Transaktion und ein Teil des aktuellen Blockchain-Kontexts für jedes Skript verfügbar. Daher kann jedes Skript überprüfen, welche Ausgaben von der Transaktion erstellt werden, ihre ERG- und Token-Mengen (wir werden diese Fähigkeit in unseren Beispiel-DEX-Verträgen nutzen), die aktuelle Blocknummer usw.
In ErgoScript definieren Sie die Bedingungen, unter denen Änderungen (d.h. das Ausgeben von Coins) in einem bestimmten Kontext erlaubt sind. Dies steht im Gegensatz zum imperativen Programmieren der Änderungen im Code eines Vertrags.
Während Ergos Transaktionsmodell eine ganze Reihe von Anwendungen wie (DEX, DeFi-Apps, LETS usw.) freischaltet, ist es nicht intuitiv, Verträge direkt als Vorbedingungen für das Ausgeben von Coins (oder Schutzskripte) zu entwerfen. In den nächsten Abschnitten werden wir eine nützliche grafische Notation betrachten, um Verträge deklarativ mithilfe von FlowCard-Diagrammen zu entwerfen, die eine visuelle Darstellung von ausführbaren Komponenten (FlowCards) sind.
FlowCards zielen darauf ab, die dApp-Entwicklung auf der Ergo-Plattform radikal zu vereinfachen, indem sie eine hochgradig deklarative Sprache, eine Ausführungsumgebung, ein Speicherformat und eine grafische Notation bereitstellen.
Wir werden mit einem hohen Niveau von Diagrammen beginnen und zur FlowCard-Spezifikation übergehen.
FlowCard-Diagramme
Die Idee hinter FlowCard-Diagrammen basiert auf den folgenden Beobachtungen: 1) Eine Ergo-Box ist unveränderlich und kann nur in der Transaktion ausgegeben werden, die sie als Eingabe verwendet. 2) Daher können wir einen Fluss von Boxen durch Transaktionen zeichnen, sodass Boxen, die in die Transaktion fließen, ausgegeben werden und die herausfließenden Boxen erstellt und zum UTXO hinzugefügt werden. 3) Eine Transaktion aus dieser Perspektive ist einfach ein Transformator alter Boxen in neue, wobei die Salden von ERGs und Tokens erhalten bleiben.
Die folgende Abbildung zeigt die Hauptelemente der Ergo-Transaktion, die wir bereits zuvor gesehen haben (jetzt unter dem Namen FlowCard-Diagramm).

Es gibt eine streng definierte Bedeutung (Semantik) hinter jedem Element des Diagramms, sodass das Diagramm eine visuelle Darstellung (oder eine Ansicht) der zugrunde liegenden ausführbaren Komponente (genannt FlowCard) ist.
Die FlowCard kann als wiederverwendbare Komponente einer Ergo-dApp verwendet werden, um die Transaktion auf der Ergo-Blockchain zu erstellen und zu initiieren. Wir werden dies in den kommenden Abschnitten besprechen.
Jetzt schauen wir uns die einzelnen Teile des FlowCard-Diagramms nacheinander an.
1. Name und Parameter
Jede FlowCard erhält einen Namen und eine Liste von typisierten Parametern. Dies ähnelt einer Vorlage mit Parametern. In der obigen Abbildung sehen wir die Send-FlowCard, die fünf Parameter hat. Die Parameter werden in der Spezifikation verwendet.
2. Vertrags-Wallet
Dies ist ein Schlüsselelement der FlowCard. Jede Box hat ein schützendes Skript. Oft ist es das Skript, das eine Signatur gegen einen öffentlichen Schlüssel überprüft. Dieses Skript ist trivial in ErgoScript und wird wie die Vorlage def pk(pubkey: Address) = { pubkey } definiert, wobei pubkey ein Parameter des Typs Address ist. In der Abbildung wird die Skriptvorlage auf den Parameter pk(sender) angewendet, und somit wird ein konkreter Wallet-Vertrag erhalten. Daher ergeben pk(sender) und pk(receiver) unterschiedliche Skripte und repräsentieren verschiedene Wallets im Diagramm, obwohl sie dieselbe Vorlage verwenden.
Contract Wallet enthält eine Menge aller UTXO-Boxen, die ein gegebenes Skript haben, das aus der gegebenen Skriptvorlage unter Verwendung der FlowCard-Parameter abgeleitet ist. Zum Beispiel ist in der Abbildung die Vorlage pk und der Parameter pubkey wird durch den FlowCard-Parameter sender ersetzt.
3. Vertrag
Obwohl ein Vertrag eine Eigenschaft einer Box ist, gruppieren wir im Diagramm die Boxen nach ihren Verträgen, sodass es so aussieht, als ob die Boxen zu den Verträgen gehören, anstatt dass die Verträge zu den Boxen gehören. In dem Beispiel haben wir drei instanziierte Verträge pk(sender), pk(receiver) und minerFee. Beachten Sie, dass pk(sender) die Instanziierung der Vorlage pk mit dem konkreten Parameter sender ist und minerFee die Instanziierung des vordefinierten Vertrags ist, der die Miner-Belohnungsboxen schützt.
4. Boxname
Im Diagramm können wir jeder Box einen Namen geben. Neben der Lesbarkeit des Diagramms verwenden wir den Namen auch als Synonym für einen komplexeren indizierten Zugriff auf die Box im Vertrag. Zum Beispiel ist change der Name der Box, die auch in den ErgoScript-Bedingungen anstelle von OUTPUTS(2) verwendet werden kann. Wir verwenden auch Boxnamen, um Ausgabebedingungen mit den Boxen zu verknüpfen.
5. Boxen in der Wallet
Im Diagramm zeigen wir Boxen (dunklere Rechtecke) als zu den Vertrags-Wallets (hellere Rechtecke) gehörend. Jedes solche Box-Rechteck ist mit einem grauen Transaktions-Rechteck durch entweder orange oder grüne Pfeile oder beide verbunden. Eine Ausgangsbox (mit einem eingehenden grünen Pfeil) kann viele Textzeilen enthalten, wobei jede Zeile eine Bedingung angibt, die als Teil der Transaktion überprüft werden sollte. Die erste Zeile gibt die Bedingung für die Menge an ERG an, die in die Box gelegt werden sollte. Andere Zeilen können eine der folgenden Formen annehmen:
amount: TOKEN- die Box sollte die gegebeneamountdes gegebenenTOKENenthalten.R == value- die Box sollte den gegebenenvaluedes gegebenen RegistersRenthalten.boxName ? condition- die Box mit dem NamenboxNamesollteconditionin ihrem Skript überprüfen.
Wir besprechen diese Bedingungen in den folgenden Abschnitten.
6. Menge an ERGs in der Box
Jede Box sollte eine Mindestmenge an ERGs speichern. Dies wird überprüft, wenn die erstellende Transaktion validiert wird. Im Diagramm wird die Menge an ERGs immer als erste Zeile angezeigt (z.B. B: ERG oder B - minErg - txFee). Die Typzuweisung B: ERG ist optional und kann zur Lesbarkeit verwendet werden. Wenn der Wert als Formel angegeben wird, muss diese Formel von der Transaktion, die die Box erstellt, respektiert werden.
Es ist wichtig zu verstehen, dass Variablen wie amount und txFee keine benannten Eigenschaften der Boxen sind. Sie sind Parameter des gesamten Diagramms und repräsentieren einige Beträge. Oder anders ausgedrückt, sie sind gemeinsame Parameter zwischen Transaktionen (z.B. Verkaufsauftrag und Tauschtransaktionen aus dem DEX-Beispiel unten teilen den Parameter tAmt). So ist der gleiche Name an den gleichen Wert im gesamten Diagramm gebunden (hier würde das Tooling sehr helfen). Wenn es jedoch um die on-chain-Validierung dieser Werte geht, werden nur explizite Bedingungen, die mit ? gekennzeichnet sind, in ErgoScript umgewandelt. Gleichzeitig werden alle anderen Bedingungen off-chain während des Transaktionsaufbaus (zum Beispiel in einer Anwendung, die die Appkit-API verwendet) und der Transaktionsvalidierung, wenn sie zur Blockchain hinzugefügt wird, sichergestellt.
7. Menge an T-Token
Eine Box kann Werte vieler Tokens speichern. Die Tokens im Diagramm sind benannt und eine value-Variable kann mit dem Token T unter Verwendung des Ausdrucks value: T verknüpft werden. Der value kann durch eine Formel angegeben werden. Wenn die Formel mit einem Boxnamen wie boxName ? formula vorangestellt ist, sollte sie auch im schützenden Skript der boxName-Box überprüft werden. Diese zusätzliche Spezifikation ist sehr praktisch, da 1) sie die automatische Validierung des visuellen Designs ermöglicht und 2) die in den Boxen eines Diagramms angegebenen Bedingungen ausreichen, um die erforderlichen Schutzskripte zu synthetisieren. (mehr dazu unten bei "Von Diagrammen zu ErgoScript-Verträgen")
8. Tx-Eingaben
Eingaben sind durch orange Pfeile mit der entsprechenden Transaktion verbunden. Ein Eingabepfeil kann ein Etikett der folgenden Formen haben:
name@index- optionaler Name mit einem Index, d.h.fee@0oder@2. Dies ist eine Eigenschaft des Zielendpunkts des Pfeils. Der Name wird in den Bedingungen der zugehörigen Boxen verwendet, und derindexist die Position der entsprechenden Box in der INPUTS-Sammlung der Transaktion.!action- ist eine Eigenschaft der Quelle des Pfeils und gibt einen Namen für einen alternativen Ausgabepfad der Box an (wir werden dies im DEX-Beispiel sehen).
Aufgrund alternativer Ausgabepfade kann eine Box viele ausgehende orange Pfeile haben, in diesem Fall sollten sie mit unterschiedlichen Aktionen gekennzeichnet werden.
9. Transaktion
Eine Transaktion gibt Eingangsboxen aus und erstellt Ausgangsboxen. Die Eingangsboxen werden durch die orange Pfeile angegeben, und die Etiketten sollen die Eingaben an den richtigen Indizes in der INPUTS-Sammlung platzieren. Die Ausgangsboxen werden durch die grünen Pfeile angegeben. Jede Transaktion sollte ein striktes Gleichgewicht der ERG-Werte (Summe der Eingaben == Summe der Ausgaben) und für jeden Token sollte die Summe der Eingaben >= der Summe der Ausgaben sein. Das Entwurfsdiagramm erfordert eine explizite Spezifikation der ERG- und Tokenwerte für alle Ausgangsboxen, um implizite Fehler zu vermeiden und die Lesbarkeit zu verbessern.
10. Tx-Ausgaben
Ausgaben sind durch grüne Pfeile mit der entsprechenden Transaktion verbunden. Ein Ausgabepfeil kann ein Etikett der folgenden Form name@index haben, wobei ein optionaler Name mit einem Index, d.h. fee@0 oder @2, begleitet wird. Dies ist eine Eigenschaft des Quellendpunkts des Pfeils. Der Name wird in den Bedingungen der zugehörigen Boxen verwendet, und der index ist die Position der entsprechenden Box in der OUTPUTS-Sammlung der Transaktion.
Beispiel: Dezentrale Börse (DEX)
Jetzt lassen Sie uns die oben beschriebene Notation verwenden, um eine FlowCard für eine DEX-dApp zu entwerfen. Es ist einfach genug, zeigt aber auch alle wichtigen Funktionen der FlowCard-Diagramme, die wir im vorherigen Abschnitt eingeführt haben.
Das dApp-Szenario wird in der folgenden Abbildung gezeigt:
Es gibt drei Teilnehmer (Käufer, Verkäufer und DEX) der DEX-dApp und fünf verschiedene Transaktionstypen, die von den Teilnehmern erstellt werden. Der Käufer möchte ergAmt von ERGs gegen tAmt von TID-Tokens (oder umgekehrt, der Verkäufer möchte TID-Tokens gegen ERGs verkaufen, wer die Bestellung zuerst sendet, spielt keine Rolle). Sowohl der Käufer als auch der Verkäufer können ihre Bestellungen jederzeit stornieren. Der DEX-Off-Chain-Matching-Service kann passende Bestellungen finden und die Swap-Transaktion erstellen, um den Austausch abzuschließen.
Das folgende Diagramm spezifiziert vollständig (und formal) alle fünf Transaktionen, die off-chain von der DEX-dApp erstellt werden müssen. Es spezifiziert auch alle Ausgabebedingungen, die on-chain überprüft werden sollten.

Lassen Sie uns das FlowCard-Diagramm und die Logik jeder Transaktion im Detail besprechen:
Kaufauftrag-Transaktion
Ein Käufer erstellt eine Buy Order-Transaktion. Die Transaktion gibt E Menge an ERGs aus (die wir als E: ERG schreiben werden) aus einer oder mehreren Boxen in der pk(buyer)-Wallet. Die Transaktion erstellt eine bid-Box mit ergAmt: ERG, die durch das buyOrder-Skript geschützt ist. Das buyOrder-Skript wird aus der Spezifikation synthetisiert (siehe unten bei "Von Diagrammen zu ErgoScript-Verträgen") entweder manuell oder automatisch durch ein Tool. Obwohl wir das buyOrder-Skript während des Designs nicht explizit definieren müssen, sollte die bid-Box zur Laufzeit das buyOrder-Skript als schützende Proposition enthalten (die die Ausgabebedingungen der Box überprüft), andernfalls werden die im Diagramm angegebenen Bedingungen nicht überprüft.
Die change-Box wird erstellt, um die Eingangs- und Ausgangssummen der Transaktion auszugleichen. Die Gebührenbox wird weggelassen, da sie automatisch von den Tools hinzugefügt werden kann. In der Praxis kann der Designer jedoch die Gebührenbox explizit zu einem Diagramm hinzufügen. Sie deckt die Fälle komplexerer Transaktionen (wie Swap) ab, bei denen es viele Möglichkeiten gibt, die Transaktionsgebühr zu zahlen.
Stornieren von Kauf- und Verkaufs-Transaktionen
Zu jeder Zeit kann der buyer die Bestellung stornieren, indem er die CancelBuy-Transaktion sendet. Die Transaktion sollte den schützenden buyOrder-Vertrag erfüllen, der die bid-Box schützt. Wie Sie im Diagramm sehen können, können sowohl die Cancel- als auch die Swap-Transaktionen die bid-Box ausgeben. Wenn eine Box Ausgabemöglichkeiten (oder Ausgabepfade) hat, wird jede Alternative durch einen eindeutigen Namen identifiziert, der mit ! vorangestellt ist (!cancel und !swap für die bid-Box). Jeder alternative Pfad hat spezifische Ausgabebedingungen. In unserem Beispiel sollte die Cancel Buy-Transaktion, wenn sie die bid-Box ausgibt, die Bedingung ?buyer erfüllen, die wir als "die Signatur für die buyer-Adresse sollte in der Transaktion vorgelegt werden" lesen. Daher kann nur der Käufer die Kaufbestellung stornieren. Diese "Signatur"-Bedingung ist nur für den alternativen Ausgabepfad !cancel erforderlich und nicht für !swap.
Verkaufsauftrag-Transaktion
Die Sell Order-Transaktion ähnelt der BuyOrder, da sie zusätzlich zu ERGs auch mit Tokens zu tun hat. Die Transaktion gibt E: ERG und T: TID-Tokens aus der Wallet des Verkäufers aus (angegeben als pk(seller)-Vertrag). Die beiden Ausgaben sind ask und change. Der Wechsel ist eine Standardbox, um die Transaktion auszugleichen. Die ask-Box enthält tAmt: TID-Tokens für den Austausch und minErg: ERG - die Mindestmenge an ERGs, die in jeder Box erforderlich sind.
Swap-Transaktion
Dies ist eine Schlüsseltransaktion im DEX-dApp-Szenario. Die Transaktion hat mehrere Ausgabebedingungen für die Eingangsboxen, und diese Bedingungen sind in den buyOrder- und sellOrder-Skripten enthalten (die überprüft werden, wenn die Transaktion zur Blockchain hinzugefügt wird). Im Diagramm sind diese Bedingungen jedoch nicht in den bid- und ask-Boxen angegeben, sondern stattdessen in den Ausgangsboxen der Transaktion definiert.
Dies ist eine Konvention zur Verbesserung der Benutzerfreundlichkeit, da die meisten Bedingungen sich auf die Eigenschaften der Ausgangsboxen beziehen. Wir könnten diese Eigenschaften in der bid-Box angeben, aber dann müssten wir komplexere Ausdrücke verwenden.
Betrachten wir die Ausgabe, die durch den Pfeil mit der Bezeichnung buyerOut@0 erstellt wurde. Dieses Etikett sagt uns, dass die Ausgabe am Index 0 in der OUTPUTS-Sammlung der Transaktion ist und dass wir in dem Diagramm auf diese Box mit dem Namen buyerOut verweisen können. Somit können wir sowohl die Box selbst als auch den Pfeil kennzeichnen, um der Box einen Namen zu geben.
Die Bedingungen, die in der buyerOut-Box angezeigt werden, haben die Form bid ? condition, was bedeutet, dass sie on-chain überprüft werden müssen, um die bid-Box auszugeben.
Die Bedingungen haben folgende Bedeutung:
tAmt: TIDerfordert, dass die BoxtAmtMenge desTID-Tokens hat.R4 == bid.iderfordert, dass das R4-Register in der Box gleich der ID derbid-Box ist.script == buyererfordert, dass diebuyerOut-Box das Skript der Wallet hat, in der sie sich im Diagramm befindet, d.h.pk(buyer).
Ähnliche Eigenschaften werden der sellerOut-Box hinzugefügt, die auf Index 1 angegeben ist, und der Name wird ihr mit dem Etikett auf der Box selbst gegeben, anstatt auf dem Pfeil.
Die Swap-Transaktion gibt zwei Boxen bid und ask aus, wobei der Ausgabepfad !swap für beide verwendet wird, jedoch im Gegensatz zu !cancel die Bedingungen auf dem Pfad nicht angegeben sind. Hier kommen die Präfixe bid ? und ask ? ins Spiel. Sie werden verwendet, damit die in den buyerOut- und sellerOut-Boxen aufgeführten Bedingungen in den Ausgabepfad !swap der bid- und ask-Boxen entsprechend verschoben werden.
Wenn Sie sich die Bedingungen der Ausgangsboxen ansehen, werden Sie sehen, dass sie genau den Austausch von Werten zwischen den Wallets des Verkäufers und des Käufers spezifizieren. Der Käufer erhält die erforderliche Menge an TID-Token und der Verkäufer erhält die entsprechende Menge an ERGs. Die Swap-Transaktion wird erstellt, wenn es zwei übereinstimmende Boxen mit buyOrder- und sellOrder-Verträgen gibt.
Von Diagrammen zu ErgoScript-Verträgen
Was an FlowCard-Spezifikationen interessant ist, ist, dass wir sie verwenden können, um automatisch die erforderlichen ErgoTree-Skripte zu generieren. Mit der entsprechenden Tool-Unterstützung kann dies automatisch erfolgen, aber bei fehlender Unterstützung kann es manuell erfolgen. Somit ermöglicht die FlowCard, alle Designentscheidungen und semantischen Details einer Ergo-dApp zu erfassen und visuell darzustellen.
Was wir als Nächstes tun werden, ist, den buyOrder-Vertrag mechanisch aus den Informationen zu erstellen, die in der DEX-FlowCard gegeben sind.
Erinnern Sie sich daran, dass jedes Skript eine Proposition (boolescher Ausdruck) ist, die true auswerten sollte, um das Ausgeben der Box zu erlauben. Wenn wir viele Bedingungen gleichzeitig erfüllen müssen, können wir sie in einer logischen Formel mit der AND-Binäroperation kombinieren, und wenn wir Alternativen haben (nicht unbedingt exklusiv), können wir sie in die OR-Operation einfügen.
Die buyOrder-Box hat die alternativen Ausgabepfade !cancel und !swap. Daher sollte der ErgoScript-Code eine OR-Operation mit zwei Argumenten haben - eines für jeden Ausgabepfad.
/** buyOrder contract */
{
val cancelCondition = {}
val swapCondition = {}
cancelCondition || swapCondition
}
Die Formel für den Ausdruck cancelCondition ist im Ausgabepfad !cancel der buyOrder-Box angegeben. Wir können sie direkt in das Skript einfügen.
/** buyOrder contract */
{
val cancelCondition = { buyer }
val swapCondition = {}
cancelCondition || swapCondition
}
Für den Ausgabepfad !swap der buyOrder-Box sind die Bedingungen in der buyerOut-Ausgangsbox der Swap-Transaktion angegeben. Wenn wir sie einfach in die swapCondition einfügen, erhalten wir ein syntaktisch inkorrektes Skript.
/** buyOrder contract */
{
val cancelCondition = { buyer }
val swapCondition = {
tAmt: TID &&
R4 == bid.id &&
@contract
}
cancelCondition || swapCondition
}
Wir können jedoch die Bedingungen aus der Diagrammsyntax in ErgoScript-Ausdrücke übersetzen, indem wir die folgenden einfachen Regeln verwenden:
buyerOut@0==>val buyerOut = OUTPUTS(0)tAmt: TID==>tid._2 == tAmtwobeitid = buyerOut.tokens(TID)R4 == bid.id==>R4 == SELF.idwobeiR4 = buyerOut.R4[Coll[Byte]].getscript == buyer==>buyerOut.propositionBytes == buyer.propBytes
Beachten Sie, dass im Diagramm TID eine Token-ID darstellt, aber ErgoScript keinen Zugriff auf die Tokens nach den IDs hat, sodass wir nicht tokens.getByKey(TID) schreiben können. Aus diesem Grund wird TID, wenn das Diagramm in ErgoScript übersetzt wird, zu einer benannten Konstante des Index in der tokens-Sammlung der Box. Der konkrete Wert der Konstante wird zugewiesen, wenn die BuyOrder-Transaktion mit der buyOrder-Box erstellt wird. Die Entsprechung und Konsistenz zwischen der tatsächlichen tokenId, der TID-Konstante und den tatsächlichen Tokens der buyerOut-Box wird durch den off-chain Anwendungs-Code sichergestellt, was völlig möglich ist, da alle Transaktionen von der Anwendung unter Verwendung von FlowCard als leitende Spezifikation erstellt werden. Dies mag zu kompliziert erscheinen, ist aber Teil der Übersetzung von Diagrammspezifikationen in tatsächlichen ausführbaren Anwendungs-Code, von dem der Großteil automatisiert werden kann.
Nach der Transformation können wir ein korrektes Skript erhalten, das alle erforderlichen Vorbedingungen für das Ausgeben der buyOrder-Box überprüft.
/** buyOrder contract */
def DEX(buyer: Addrss, seller: Address, TID: Int, ergAmt: Long, tAmt: Long)
{
val cancelCondition: SigmaProp = { buyer } // Überprüfen der Signatur des Käufers (ProveDlog)
val swapCondition = OUTPUTS.size > 0 && { // Sicherstellen des Zugriffs auf OUTPUTS
val buyerOut = OUTPUTS(0) // von buyerOut@0
buyerOut.tokens.size > TID && { // Sicherstellen des Zugriffs auf Tokens
val tid = buyerOut.tokens(TID)
val regR4 = buyerOut.R4[Coll[Byte]]
regR4.isDefined && { // Sicherstellen des Zugriffs auf R4
val R4 = regR4.get
tid._2 == tAmt && // von tAmt: TID
R4 == SELF.id && // von R4 == bid.id
buyerOut.propositionBytes == buyer.propBytes // von script == buyer
}
}
}
cancelCondition || swapCondition
}
Ein ähnliches Skript für die sellOrder-Box kann unter Verwendung der gleichen Übersetzungsregeln erhalten werden. Mit Hilfe des Toolings kann der Code der Verträge mechanisch aus der Diagrammspezifikation generiert werden.
Fazit
Deklarative Programmiermodelle haben bereits in vielen Anwendungsbereichen wie Big Data, Stream Processing, Deep Learning, Datenbanken usw. den Kampf gegen imperative Programmierung gewonnen. Ergo ist Pionier im deklarativen Modell der dApp-Entwicklung als bessere und sicherere Alternative zum derzeit beliebten imperativen Modell von Smart Contracts.
Das Konzept der FlowCard verlagert den Fokus vom Schreiben von ErgoScript-Verträgen auf den gesamten Fluss von Werten (daher der Name), sodass ErgoScript immer aus ihnen generiert werden kann. Sie müssen den ErgoScript-Code nie ansehen, sobald das Tooling vorhanden ist.
Hier sind die möglichen nächsten Schritte für zukünftige Arbeiten:
-
Speicherformat für FlowCard-Spezifikation und das entsprechende standardisierte EIP-Dateiformat (Json/XML/Protobuf). Dies wird verschiedenen Tools (Diagrammeditor, Laufzeit, dApps usw.) ermöglichen,
*.flowcard-Dateien zu erstellen und zu verwenden. -
FlowCard-Viewer, der die Diagramme aus
*.flowcard-Dateien generieren kann. -
FlowCard-Runtime, die
*.flowcard-Dateien ausführen, Transaktionen erstellen und an das Ergo-Netzwerk senden kann. -
FlowCard-Designer-Tool, das die Entwicklung komplexer Diagramme vereinfachen kann. Dies wird das Entwerfen und Validieren von Ergo-Verträgen zu einem angenehmen Erlebnis machen, mehr wie Zeichnen als Programmieren. Darüber hinaus kann die Richtigkeit des gesamten dApp-Szenarios durch das Tooling überprüft und kontrolliert werden.
Referenzen
Share post
13. August 2025
12. August 2025
9. Juli 2025
12. Mai 2025
9. Dezember 2024
19. August 2024
