Sie haben sicherlich von Telegram gehört, zu starten. Doch vielleicht haben Sie die Nachricht verpasst, dass Telegram zur Umsetzung eines oder mehrerer Smart Contracts für diese Plattform angekündigt hat.
Das Team von Serokell, das über umfangreiche Erfahrung in der Entwicklung großer Blockchain-Projekte verfügt, konnte nicht untätig bleiben. Wir haben fünf Mitarbeiter für den Wettbewerb delegiert, und schon nach zwei Wochen belegten sie unter dem (nicht) bescheidenen zufälligen Nicknamen Sexy Chameleon den ersten Platz. In diesem Artikel werde ich erläutern, wie sie dies geschafft haben. Wir hoffen, dass Sie in den nächsten zehn Minuten zumindest eine interessante Geschichte lesen und im besten Fall etwas Nützliches finden, das Sie in Ihrer eigenen Arbeit anwenden können.
Aber lassen Sie uns mit einem kleinen Kontext beginnen.
Der Wettbewerb und seine Bedingungen
Die Hauptaufgaben der Teilnehmer bestanden darin, einen oder mehrere der vorgeschlagenen Smart Contracts umzusetzen sowie Vorschläge zur Verbesserung des TON-Ökosystems einzubringen. Der Wettbewerb fand vom 24. September bis zum 15. Oktober statt, und die Ergebnisse wurden erst am 15. November bekannt gegeben. Das ist ziemlich lange, zumal Telegram in der Zwischenzeit bereits die Ergebnisse der Wettbewerbe im Design und in der Entwicklung von Anwendungen in C++ zur Testung und Bewertung der VoIP-Qualität in Telegram verkündet hat.
Wir haben aus der von den Organisatoren vorgeschlagenen Liste zwei Smart Contracts ausgewählt. Für einen von ihnen haben wir die Tools genutzt, die zusammen mit TON bereitgestellt werden, und den zweiten in einer neuen Sprache umgesetzt, die unsere Ingenieure speziell für TON entwickelt und in Haskell integriert haben.
Die Wahl der funktionalen Programmiersprache ist kein Zufall. In unserem berichten wir häufig darüber, warum wir die Komplexität funktionaler Sprachen für stark übertrieben halten und warum wir insgesamt objektorientierten Sprachen den Vorzug geben. Übrigens gibt es darin auch den .
Warum haben wir uns überhaupt entschieden teilzunehmen?
Kurz gesagt: Unsere Spezialisierung liegt in der Entwicklung maßgeschneiderter und komplexer Projekte, die besondere Fähigkeiten erfordern und oft wissenschaftlichen Wert für die IT-Community darstellen. Wir unterstützen aktiv die Open-Source-Entwicklung, fördern deren Verbreitung und arbeiten mit führenden Universitäten in Russland im Bereich Informatik und Mathematik zusammen.
Die spannenden Aufgaben des Wettbewerbs und die Zugehörigkeit zu unserem geliebten Projekt Telegram waren bereits eine großartige Motivation, während der Preisfonds einen zusätzlichen Anreiz bot. 🙂
Erforschung der TON-Blockchain
Wir verfolgen intensiv die neuesten Entwicklungen in der Blockchain-Technologie, Künstlicher Intelligenz und maschinellem Lernen und bemühen uns, keine relevanten Veröffentlichungen in den Bereichen, in denen wir aktiv sind, zu verpassen. Daher war unser Team zum Zeitpunkt des Wettbewerbs bereits mit den Ideen aus vertraut. Bevor wir jedoch mit TON arbeiteten, hatten wir die technische Dokumentation und den tatsächlichen Quellcode der Plattform nicht analysiert, weshalb der erste Schritt naheliegend war: eine gründliche Untersuchung der offiziellen Dokumentation. auch in .
Da der Code bereits zu Beginn des Wettbewerbs veröffentlicht wurde, haben wir beschlossen, um Zeit zu sparen, nach einer Anleitung oder Zusammenfassung zu suchen, die von Nutzerngeschrieben wurde. Leider führte dies zu keinem Ergebnis – außer einer Anleitung zur Erstellung der Plattform auf Ubuntu fanden wir kein weiteres Material.
Die Dokumentation selbst war umfassend, jedoch war es in einigen Punkten schwierig, sie zu lesen. Häufig mussten wir zu bestimmten Abschnitten zurückkehren und von hohen abstrakten Beschreibungen zu detaillierten Implementierungsdetails wechseln.
Es wäre einfacher gewesen, wenn es in der Spezifikation gar keine detaillierte Beschreibung der Implementierung gegeben hätte. Informationen darüber, wie die virtuelle Maschine ihren Stack darstellt, lenken eher die Entwickler ab, die Smart Contracts für die TON-Plattform erstellen, als dass sie ihnen helfen.
Nix: Projekt erstellen
Bei Serokell sind wir große Fans . Wir entwickeln unsere Projekte mit , und auf all unseren Servern ist installiert. Dadurch sind all unsere Builds reproduzierbar und laufen auf jedem Betriebssystem, auf dem Nix installiert werden kann.
Deshalb haben wir mit der Erstellung begonnen . Mit ihm lässt sich TON ganz einfach 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 für Sie, egal ob Sie NixOS, Ubuntu oder macOS verwenden.
Programmierung für TON
Der Code von Smart Contracts im TON Netzwerk läuft auf der TON Virtual Machine (TVM). TVM ist komplizierter als die meisten anderen virtuellen Maschinen und bietet einige interessante Funktionen, darunter die Fähigkeit, mit Fortsetzungen (continuations) zu arbeiten und Datenverweisen umzugehen..
Außerdem haben die Entwickler von TON gleich drei neue Programmiersprachen geschaffen:
Fift — eine universelle stackbasierte Programmiersprache, die erinnert an . Ihre Superkraft ist die Interaktion mit der TVM.
FunC — eine Programmiersprache für Smart Contracts, die ähnlich ist wie und in eine weitere Sprache, Fift Assembler, kompiliert wird.
Fift Assembler — eine Fift-Bibliothek zur Generierung von binärem ausführbarem Code für die TVM. Fift Assembler hat keinen Compiler. Es handelt sich um eine .
Unsere Wettbewerbseinträge
Endlich ist es an der Zeit, die Ergebnisse unserer Bemühungen zu betrachten.
Asynchroner Zahlungskanal
Ein Zahlungskanal (payment channel) ist ein Smart Contract, der es zwei Nutzern ermöglicht, Zahlungen außerhalb der Blockchain zu tätigen. Dadurch spart man nicht nur Geld (keine Gebühren), sondern auch Zeit (man muss nicht warten, bis der nächste Block verarbeitet wird). Zahlungen können beliebig klein sein und so oft geschehen, wie es nötig ist. Dabei müssen die Parteien einander nicht vertrauen, da die Fairness des endgültigen Abrechnungsprozesses durch den Smart Contract gewährleistet ist.
Wir haben eine recht einfache Lösung für das Problem gefunden. Zwei Parteien können signierte Nachrichten austauschen, die jeweils zwei Zahlen enthalten – den Gesamtbetrag, den jeder Teilnehmer gezahlt hat. Diese beiden Zahlen fungieren als in traditionellen verteilten Systemen und bestimmen die Reihenfolge "geschah vor" in Transaktionen. Mit diesen Daten kann der Vertrag mögliche Konflikte lösen.
Um diese Idee tatsächlich umzusetzen, genügt eine einzige Zahl. Wir haben jedoch beide Zahlen beibehalten, um eine benutzerfreundlichere Oberfläche zu schaffen. Zudem haben wir beschlossen, die Zahlungshöhe in jede Nachricht aufzunehmen. Ohne diese Information könnte der Nutzer, falls eine Nachricht aus irgendeinem Grund verloren geht, die Zahlung zwar korrekt abrufen, jedoch die gesendete Summe nicht bemerken.
Um unsere Idee zu testen, 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, der jedoch nur für unidirektionale Kanäle gedacht ist.
- , in dem dieselbe Idee präsentiert wird wie bei uns, jedoch ohne viele wichtige Details zu erläutern, wie etwa die allgemeine Korrektheit und das Verfahren zur Konfliktlösung.
Es wurde deutlich, dass es sinnvoll ist, unser Protokoll detailliert zu beschreiben und dabei besonderen Wert auf seine Korrektheit zu legen. Nach mehreren Iterationen war die Spezifikation fertig, und jetzt können auch Sie .
Wir haben einen Vertrag auf FunC realisiert und das Befehlszeilenwerkzeug zur Interaktion mit unserem Vertrag vollständig in Fift geschrieben, wie es von den Veranstaltern empfohlen wurde. Wir hätten jede andere Sprache für unser CLI wählen können, aber es hat uns interessiert, Fift auszuprobieren, um zu sehen, wie es sich in der Praxis schlägt.
Ehrlich gesagt haben wir, nachdem wir mit Fift gearbeitet haben, keine überzeugenden Gründe gesehen, diese Sprache den beliebten und weit verbreiteten Sprachen mit ausgereiften Werkzeugen und Bibliotheken vorzuziehen. Die Programmierung in einer Stack-Sprache ist ziemlich unangenehm, da man ständig im Kopf haben muss, was wo im Stack liegt, und der Compiler bietet dabei keinerlei Unterstützung.
Daher sehen wir die einzige berechtigte Existenz von Fift in seiner Rolle als Wirtssprache für den Fift Assembler. 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, über unseren zweiten Smart Contract zu berichten. Wir haben uns entschieden, eine Multisignatur-Wallet zu entwickeln, aber einen weiteren Smart Contract in FunC zu schreiben, wäre einfach zu langweilig gewesen. Wir wollten etwas Besonderes hinzufügen, und das ist unsere eigene Assemblersprache für die TVM.
Wie der Fift Assembler ist unsere neue Sprache eingebettet, jedoch haben wir als Host Haskell gewählt, wodurch wir sein fortschrittliches Typsystem vollständig nutzen konnten. Bei der Arbeit mit Smart Contracts, wo bereits kleine Fehler sehr kostspielig sein können, sehen wir statische Typisierung als großen Vorteil.
Um zu demonstrieren, wie der in Haskell eingebettete TVM-Assembler aussieht, haben wir damit eine Standard-Wallet implementiert. Hier sind einige Punkte, die Sie beachten sollten:
- Dieser Vertrag besteht aus einer Funktion, aber Sie können beliebig viele verwenden. Wenn Sie eine neue Funktion in der Host-Sprache (d.h. in Haskell) definieren, ermöglicht Ihnen unser eDSL die Wahl, ob sie in ein separates Unterprogramm in der TVM umgewandelt werden soll oder einfach an der Stelle des Aufrufs eingebettet wird.
- Wie in Haskell haben Funktionen Typen, die zur Compile-Zeit überprüft werden. In unserem eDSL ist der Eingangstyp einer Funktion der Typ des Stacks, den die Funktion erwartet, und der Rückgabetypt ist der Typ des Stacks, der nach dem Aufruf entsteht.
- Im Code gibt es Annotationen
stacktype, die den erwarteten Typ des Stacks an der Aufrufstelle beschreiben. Im ursprünglichen Wallet-Vertrag waren dies einfach Kommentare, aber in unserem eDSL sind sie tatsächlich Teil des Codes und werden zur Compile-Zeit überprüft. Sie können als Dokumentation oder Behauptungen dienen, die dem Entwickler helfen, ein Problem zu finden, falls sich der Stacktyp bei Änderungen am Code ändert. Natürlich wirkt sich solch eine Annotation nicht auf die Laufzeitleistung aus, da kein TVM-Code für sie generiert wird. - Es handelt sich immer noch um einen Prototyp, der in zwei Wochen geschrieben wurde, sodass noch viel Arbeit an dem Projekt zu leisten ist. Zum Beispiel müssen alle Instanzen von 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 "Unbekannte 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 den Multisignatur-Wallet-Vertrag finden Sie in Ein detaillierterer Bericht Ergebnisse des Wettbewerbs und TON
Insgesamt haben wir 380 Stunden in unsere Arbeit investiert (einschließlich Einarbeitung in die Dokumentation, Besprechungen und die eigentliche Entwicklung). Fünf Entwickler haben an dem Wettbewerbsprojekt teilgenommen: CTO, Teamleiter, Blockchain-Plattform-Spezialisten und Softwareentwickler für Haskell.
Die Ressourcen für die Teilnahme am Wettbewerb fanden wir mühelos, da der Geist eines Hackathons, enge Teamarbeit und die Notwendigkeit eines schnellen Einarbeitens in neue Technologien immer faszinierend sind. Mehrere schlaflose Nächte, um maximale Ergebnisse unter eingeschränkten Ressourcen zu erzielen, werden durch unschätzbare Erfahrungen und großartige Erinnerungen ausgeglichen. Darüber hinaus ist die Arbeit an solchen Aufgaben eine hervorragende Prüfung der Unternehmensprozesse, da es äußerst schwierig ist, wirklich respektable Ergebnisse ohne ein hervorragend abgestimmtes internes Zusammenspiel zu erzielen.
Die Ressourcen für die Teilnahme am Contest haben wir mühelos gefunden, da der Geist eines Hackathons, enge Teamarbeit und die Notwendigkeit, sich schnell in die Aspekte neuer Technologien einzuarbeiten, immer spannend sind. Mehrere schlaflose Nächte, um unter den Bedingungen knapper Ressourcen das bestmögliche Ergebnis zu erzielen, werden durch wertvolle Erfahrungen und großartige Erinnerungen belohnt. Außerdem ist die Arbeit an solchen Aufgaben immer eine gute Gelegenheit, die internen Prozesse des Unternehmens zu überprüfen, da es sehr schwierig ist, wirklich lohnenswerte Ergebnisse ohne perfekt abgestimmte interne Kommunikation zu erzielen.
Neben der Lyrik: Wir waren beeindruckt von dem Umfang der Arbeit, die das Team von TON geleistet hat. Ihnen ist es gelungen, ein komplexes, schönes und vor allem funktionierendes System zu entwickeln. TON hat sich als Plattform mit großem Potenzial erwiesen. Damit dieses Ökosystem jedoch weiter wachsen kann, gibt es noch viel zu tun, sowohl in Bezug auf seine Nutzung in Blockchain-Projekten als auch hinsichtlich der Verbesserung der Entwicklungstools. Wir sind stolz darauf, nun Teil dieses Prozesses zu sein.
Wenn Sie nach dem Lesen dieses Artikels noch Fragen haben oder Ideen dazu, wie Sie TON für Ihre Aufgaben nutzen können, — wir teilen gerne unser Wissen.
Quelle: habr.com
