Sie haben sicherlich gehört, dass Telegram Aber vielleicht haben Sie die Nachricht verpasst, dass Telegram zur Umsetzung eines oder mehrerer Smart Contracts für diese Plattform.
Das Serokell-Team mit umfangreicher Erfahrung in der Entwicklung großer Blockchain-Projekte konnte sich nicht zurückhalten. Wir haben fünf Mitarbeiter zum Wettbewerb delegiert, und bereits nach zwei Wochen erreichten sie den ersten Platz mit dem (nicht) bescheidenen zufälligen Spitznamen Sexy Chameleon. In diesem Artikel werde ich erzählen, wie ihnen das gelungen ist. Wir hoffen, dass Sie in den nächsten zehn Minuten zumindest eine interessante Geschichte lesen und bestenfalls etwas Nützliches finden, das Sie in Ihrer Arbeit anwenden können.
Aber lassen Sie uns mit einem kleinen Einblick in den Kontext beginnen.
Der Wettbewerb und seine Bedingungen
Die Hauptaufgaben der Teilnehmer bestanden also darin, einen oder mehrere der vorgeschlagenen Smart Contracts zu implementieren und Vorschläge zur Verbesserung des TON-Ökosystems einzubringen. Der Wettbewerb fand vom 24. September bis 15. Oktober statt, die Ergebnisse wurden jedoch erst am 15. November bekannt gegeben. Ziemlich lange, wenn man bedenkt, dass Telegram in dieser Zeit auch die Ergebnisse von Wettbewerben im Design und in der Entwicklung von Anwendungen in C++ zur Testung und Bewertung der Qualität von VoIP-Anrufen in Telegram durchgeführt und bekannt gegeben hat.
Wir haben aus der von den Organisatoren vorgeschlagenen Liste zwei Smart Contracts ausgewählt. Für einen von ihnen haben wir die Werkzeuge verwendet, die zusammen mit TON bereitgestellt wurden, während der andere in einer neuen Sprache implementiert wurde, die unsere Ingenieure speziell für TON entwickelt und in Haskell integriert haben.
Die Wahl der funktionalen Programmiersprache ist nicht zufällig. In unserem erläutern wir häufig, warum wir die Komplexität funktionaler Sprachen für stark übertrieben halten und warum wir sie insgesamt objektorientierten Sprachen vorziehen. Übrigens gibt es darin auch den .
Warum wir überhaupt entschieden haben, teilzunehmen
Kurz gesagt, weil unsere Spezialisierung auf nicht standardmäßige und komplexe Projekte abzielt, die besondere Fähigkeiten erforden und häufig wissenschaftlichen Wert für die IT-Community darstellen. Wir unterstützen die Open-Source-Entwicklung leidenschaftlich und setzen uns für deren Popularisierung ein, zudem arbeiten wir mit führenden Universitäten Russlands im Bereich der Computerwissenschaften und Mathematik zusammen.
Die interessanten Aufgaben des Wettbewerbs und die Zugehörigkeit zu unserem heiß geliebten Projekt Telegram waren bereits eine großartige Motivation, und der Preisfonds wurde zu einem zusätzlichen Anreiz. 🙂
Untersuchung der Blockchain TON
Wir verfolgen aufmerksam die neuen Entwicklungen in der Blockchain, künstlichen Intelligenz und im maschinellen Lernen und versuchen, keinen nennenswerten Release in den Bereichen, in denen wir tätig sind, zu verpassen. Daher war unser Team zum Zeitpunkt des Wettbewerbs bereits mit den Ideen aus vertraut. Allerdings hatten wir vor Beginn der Arbeit mit TON die technische Dokumentation und den tatsächlichen Quellcode der Plattform nicht analysiert, daher war der erste Schritt ziemlich offensichtlich – eine gründliche Untersuchung der offiziellen Dokumentation auf und in .
Zu Beginn des Wettbewerbs war der Code bereits veröffentlicht, daher haben wir beschlossen, um Zeit zu sparen, nach einer Anleitung oder Zusammenfassung zu suchen, die von Benutzerngeschrieben wurde. Leider war dies nicht erfolgreich – außer der Anleitung zum Zusammenstellen der Plattform auf Ubuntu haben wir keine weiteren Materialien gefunden.
Die Dokumentation selbst stellte sich als sorgfältig ausgearbeitet heraus, allerdings war es manchmal schwierig, sie zu lesen. Oft mussten wir zu bestimmten Punkten zurückkehren und uns von hochrangigen Beschreibungen abstrakter Ideen auf nieder-rangige Details der Implementierung umschalten.
Es wäre einfacher gewesen, wenn die Spezifikation überhaupt keine detaillierte Beschreibung der Implementierung enthalten hätte. Informationen darüber, wie die virtuelle Maschine ihren Stack darstellt, lenken eher von den Entwicklern ab, die Smart Contracts für die TON-Plattform erstellen, als dass sie ihnen helfen.
Nix: wir stellen das Projekt zusammen
Bei Serokell sind wir große Fans von. Wir bauen unsere Projekte damit und setzen sie mit Hilfe von um, und auf all unseren Servern ist installiert . Dadurch sind alle unsere Builds reproduzierbar und funktionieren auf jedem Betriebssystem, auf dem Nix installiert werden kann.
Deshalb haben wir mit der Erstellung eines begonnen. Damit ist es maximal einfach, TON zu kompilieren:
$ cd ~/.config/nixpkgs/overlays && git clone https://github.com/serokell/ton.nix
$ cd /path/to/ton/repo && nix-shell
[nix-shell]$ cmakeConfigurePhase && makeBeachten Sie, dass Sie keine Abhängigkeiten installieren müssen. Nix erledigt alles auf magische Weise für Sie, unabhängig davon, ob Sie NixOS, Ubuntu oder macOS verwenden.
Programmierung für TON
Der Code der Smart Contracts im TON-Netzwerk wird auf der TON Virtual Machine (TVM) ausgeführt. TVM ist komplexer als die meisten anderen virtuellen Maschinen und bietet sehr interessante Funktionen, wie zum Beispiel die Fähigkeit, mit Fortsetzungen (continuations) und Daten-Links.
Darüber hinaus haben die Leute von TON gleich drei neue Programmiersprachen entwickelt:
Fift — eine universelle stackbasierte Programmiersprache, die an erinnert. Ihre Superfähigkeit ist die Interaktion mit der TVM.
FunC — eine Programmiersprache für Smart Contracts, die ähnlich ist wie und in eine weitere Sprache namens Fift Assembler kompiliert wird.
Fift Assembler — eine Fift-Bibliothek zur Generierung von binärem ausführbarem Code für TVM. Fift Assembler hat keinen Compiler. Es handelt sich um .
Unsere Wettbewerbsergebnisse
Es ist endlich an der Zeit, die Ergebnisse unserer Bemühungen zu betrachten.
Asynchroner Zahlungsweg
Ein Zahlungsweg (payment channel) ist ein Smart Contract, der es zwei Nutzern ermöglicht, Zahlungen außerhalb der Blockchain zu senden. Dadurch werden nicht nur Geld (keine Gebühren) gespart, sondern auch Zeit (man muss nicht warten, bis der nächste Block verarbeitet wird). Zahlungen können beliebig klein sein und so oft erfolgen, wie es erforderlich ist. Dabei müssen sich die Parteien nicht gegenseitig vertrauen, da die Fairness der endgültigen Abrechnung durch den Smart Contract garantiert ist.
Wir haben eine recht einfache Lösung für das Problem gefunden. Die beiden Parteien können unterzeichnete Nachrichten austauschen, von denen jede zwei Zahlen enthält — die Gesamtsumme, die jeder der Teilnehmer bezahlt hat. Diese beiden Zahlen funktionieren wie in traditionellen verteilten Systemen und legen die Reihenfolge "geschah davor" bei den Transaktionen fest. Mithilfe dieser Daten kann der Vertrag jeden möglichen Konflikt lösen.
Tatsächlich genügt zur Umsetzung dieser Idee eine Zahl, aber wir haben beide beibehalten, da wir so eine benutzerfreundlichere Schnittstelle erstellen konnten. Darüber hinaus haben wir beschlossen, die Größe der Zahlung in jede Nachricht aufzunehmen. Ohne diese Information könnte der Benutzer im Falle eines Verlusts der Nachricht, wenn auch alle Beträge und die endgültige Abrechnung korrekt wären, den Verlust möglicherweise nicht bemerken.
Um unsere Idee zu überprüfen, haben wir nach Beispielen für die Verwendung eines so einfachen und prägnanten Zahlungsprotokolls gesucht. Zu unserer Überraschung fanden wir nur zwei:
- einen ähnlichen Ansatz, jedoch für einen einseitigen Kanal.
- , in dem dieselbe Idee beschrieben wird wie bei uns, jedoch ohne Erklärung vieler wichtiger Details, wie allgemeine Korrektheit und die Konfliktlösungsverfahren.
Es wurde klar, dass es sinnvoll ist, unser Protokoll ausführlich zu beschreiben und besonderes Augenmerk auf seine Korrektheit zu legen. Nach mehreren Iterationen war die Spezifikation fertig, und jetzt könnt ihr auch .
Wir haben einen Vertrag in FunC implementiert, und das Kommandozeilenwerkzeug zur Interaktion mit unserem Vertrag haben wir vollständig in Fift geschrieben, wie es die Organisatoren empfohlen haben. Wir hätten jede andere Sprache für unser CLI wählen können, aber wir fanden es interessant, es mit Fift auszuprobieren, um zu sehen, wie es sich in der Praxis schlägt.
Ehrlich gesagt, nach der Arbeit mit Fift, haben wir keine überzeugenden Gründe gesehen, diese Sprache populären und weitverbreiteten Sprachen mit ausgereiften Werkzeugen und Bibliotheken vorzuziehen. Das Programmieren in einer Stack-Sprache ist ziemlich unangenehm, da man ständig im Kopf behalten muss, was wo im Stack liegt, und der Compiler dabei nicht hilft.
Daher sehen wir den einzigen Grund für die Existenz von Fift in seiner Rolle als Host-Sprache für den Fift Assembler. Aber wäre es nicht besser gewesen, den TVM Assembler in eine bestehende Sprache zu integrieren, anstatt eine neue für diesen, im Grunde genommen, einzigen Zweck zu entwickeln?
TVM Haskell eDSL
Jetzt ist es an der Zeit, von unserem zweiten Smart Contract zu erzählen. Wir haben beschlossen, eine Wallet mit Mehrfachsignatur zu entwickeln, aber einen weiteren Smart Contract in FunC zu schreiben, wäre zu langweilig gewesen. Wir wollten etwas Besonderes hinzufügen und das wurde unsere eigene Assemblersprache für TVM.
Wie der Fift Assembler ist auch unsere neue Sprache eingebettet, jedoch haben wir als Host statt Fift Haskell gewählt. Dadurch konnten wir sein fortschrittliches Typsystem vollständig nutzen. Bei der Arbeit mit Smart Contracts, bei denen bereits kleine Fehler sehr kostspielig sein können, halten wir statische Typisierung für einen großen Vorteil.
Um zu demonstrieren, wie der in Haskell integrierte TVM-Assembler aussieht, haben wir eine Standard-Wallet mit ihm umgesetzt. Hier sind einige Punkte, auf die Sie achten sollten:
- Dieser Vertrag besteht aus einer Funktion, aber Sie können so viele verwenden, wie Sie möchten. Wenn Sie eine neue Funktion in der Host-Sprache (d.h. in Haskell) definieren, ermöglicht Ihnen unser eDSL zu wählen, ob sie in ein separates Unterprogramm in TVM verwandelt werden soll oder einfach am Aufrufort eingebettet wird.
- Wie in Haskell haben Funktionen Typen, die während der Kompilierung überprüft werden. In unserem eDSL ist der Eingabetyp der Funktion der Typ des Stacks, den die Funktion erwartet, und der Rückgabewert ist der Typ des Stacks, der nach dem Aufruf folgt.
- Im Code gibt es Anmerkungen
stacktype, die den erwarteten Typ des Stacks am Aufrufort beschreiben. Im Originalvertrag der Wallet waren dies einfach Kommentare, aber in unserem eDSL sind sie tatsächlich Teil des Codes und werden während der Kompilierung überprüft. Sie können als Dokumentation oder Behauptungen dienen, die dem Entwickler helfen, ein Problem zu finden, falls sich der Typ des Stacks bei einer Codeänderung ändert. Natürlich wirken sich solche Anmerkungen nicht auf die Laufzeitleistung aus, da für sie kein TVM-Code generiert wird. - Es handelt sich immer noch um einen Prototyp, der in zwei Wochen erstellt wurde, sodass im Projekt noch viel Arbeit vor uns liegt. Zum Beispiel sollten alle Instanzen der Klassen, die Sie im folgenden Code sehen, automatisch generiert werden.
So sieht die Implementierung einer Multisig-Wallet in unserem eDSL aus:
main :: IO ()
main = putText $ pretty $ declProgram procedures methods
where
procedures =
[ ("recv_external", decl recvExternal)
, ("recv_internal", decl recvInternal)
]
methods =
[ ("seqno", declMethod getSeqno)
]
data Storage = Storage
{ sCnt :: Word32
, sPubKey :: PublicKey
}
instance DecodeSlice Storage where
type DecodeSliceFields Storage = [PublicKey, Word32]
decodeFromSliceImpl = do
decodeFromSliceImpl @Word32
decodeFromSliceImpl @PublicKey
instance EncodeBuilder Storage where
encodeToBuilder = do
encodeToBuilder @Word32
encodeToBuilder @PublicKey
data WalletError
= SeqNoMismatch
| SignatureMismatch
deriving (Eq, Ord, Show, Generic)
instance Exception WalletError
instance Enum WalletError where
toEnum 33 = SeqNoMismatch
toEnum 34 = SignatureMismatch
toEnum _ = error "Uknown MultiSigError id"
fromEnum SeqNoMismatch = 33
fromEnum SignatureMismatch = 34
recvInternal :: '[Slice] :-> '[]
recvInternal = drop
recvExternal :: '[Slice] :-> '[]
recvExternal = do
decodeFromSlice @Signature
dup
preloadFromSlice @Word32
stacktype @[Word32, Slice, Signature]
-- cnt cs sign
pushRoot
decodeFromCell @Storage
stacktype @[PublicKey, Word32, Word32, Slice, Signature]
-- pk cnt' cnt cs sign
xcpu @1 @2
stacktype @[Word32, Word32, PublicKey, Word32, Slice, Signature]
-- cnt cnt' pk cnt cs sign
equalInt >> throwIfNot SeqNoMismatch
push @2
sliceHash
stacktype @[Hash Slice, PublicKey, Word32, Slice, Signature]
-- hash pk cnt cs sign
xc2pu @0 @4 @4
stacktype @[PublicKey, Signature, Hash Slice, Word32, Slice, PublicKey]
-- pubk sign hash cnt cs pubk
chkSignU
stacktype @[Bool, Word32, Slice, PublicKey]
-- ? cnt cs pubk
throwIfNot SignatureMismatch
accept
swap
decodeFromSlice @Word32
nip
dup
srefs @Word8
pushInt 0
if IsEq
then ignore
else do
decodeFromSlice @Word8
decodeFromSlice @(Cell MessageObject)
stacktype @[Slice, Cell MessageObject, Word8, Word32, PublicKey]
xchg @2
sendRawMsg
stacktype @[Slice, Word32, PublicKey]
endS
inc
encodeToCell @Storage
popRoot
getSeqno :: '[] :-> '[Word32]
getSeqno = do
pushRoot
cToS
preloadFromSlice @Word32Den vollständigen Quellcode unseres eDSL und des Wallet-Vertrags mit Mehrfachsignatur finden Sie in Und detaillierter über eingebettete Sprachen gesprochen.
Die Ergebnisse des Wettbewerbs und TON
Insgesamt hat unsere Arbeit 380 Stunden in Anspruch genommen (einschließlich der Einarbeitung in die Dokumentation, Besprechungen und der eigentlichen Entwicklung). An dem Wettbewerbsprojekt waren fünf Entwickler beteiligt: CTO, Teamleiter, Spezialisten für Blockchain-Plattformen und Softwareentwickler in Haskell.
Die Ressourcen für die Teilnahme am Wettbewerb haben wir ohne Mühe gefunden, denn der Geist eines Hackathons, enge Teamarbeit und die Notwendigkeit, sich schnell in neue Technologien einzuarbeiten, sind immer aufregend. Mehrere schlaflose Nächte, um im Rahmen knapper Ressourcen das bestmögliche Ergebnis zu erzielen, werden durch wertvolle Erfahrungen und großartige Erinnerungen belohnt. Zudem ist die Arbeit an solchen Aufgaben immer eine gute Prüfung der internen Prozesse des Unternehmens, da es äußerst schwierig ist, wirklich gute Ergebnisse ohne perfekte interne Zusammenarbeit zu erzielen.
Abgesehen von der Lyrik: Wir waren von dem Umfang der Arbeiten, die das Team von TON geleistet hat, beeindruckt. Ihnen ist es gelungen, ein komplexes, ansprechendes und vor allem funktionierendes System aufzubauen. TON hat sich als eine Plattform mit großem Potenzial erwiesen. Damit sich dieses Ökosystem weiterentwickeln kann, muss jedoch noch viel getan werden, sowohl hinsichtlich der Nutzung in Blockchain-Projekten als auch bei der Verbesserung der Entwicklungstools. Wir sind stolz darauf, jetzt Teil dieses Prozesses zu sein.
Wenn Sie nach dem Lesen dieses Artikels noch Fragen haben oder Ideen, wie Sie TON zur Lösung Ihrer Aufgaben einsetzen können, — teilen wir gerne unsere Erfahrungen.
Quelle: habr.com
