Určitě jste slyšeli, že Telegram . Mohli jste si ale nevšimnout zprávy, že nedávno Telegram na implementaci jednoho nebo několika chytrých kontraktů pro tuto platformu.
Tým Serokell s bohatými zkušenostmi v oblasti vývoje velkých blockchainových projektů se nemohl postavit stranou. Na soutěž jsme delegovali pět zaměstnanců a již za dvě týdny obsadili první místo pod (ne)skromným náhodným nickem Sexy Chameleon. V tomto článku se podělím o to, jak se to podařilo. Doufáme, že během následujících deseti minut si přečtete zajímavý příběh, a maximálně v něm najdete něco užitečného, co můžete aplikovat ve své práci.
Ale začněme malým ponořením do kontextu.
Soutěž a její podmínky
Hlavními úkoly účastníků bylo implementovat jeden nebo více navržených chytrých kontraktů a také předložit návrhy na zlepšení ekosystému TON. Soutěž probíhala od 24. září do 15. října a výsledky byly oznámeny až 15. listopadu. Poměrně dlouho, když vezmeme v úvahu, že během této doby Telegram stihl provést a oznámit výsledky soutěží v designu a vývoji aplikací v C++ pro testování a hodnocení kvality VoIP hovorů v Telegramu.
Vybrali jsme ze seznamu navrženého organizátory dva chytré kontrakty. Pro jeden z nich jsme použili nástroje, které byly distribuovány spolu s TON, a druhý jsme realizovali v novém jazyce, který naši inženýři vyvinuli speciálně pro TON a který je integrován do Haskellu.
Výběr funkcionálního programovacího jazyka není náhodný. V našem často říkáme, proč považujeme složitost funkcionálních jazyků za velké přehánění a proč je obecně preferujeme před těmi objektově orientovanými. Mimochodem, v něm je i .
Proč jsme se vůbec rozhodli účastnit
Stručně řečeno, naše specializace spočívá ve speciálních a složitých projektech, které vyžadují zvláštní dovednosti a často představují vědeckou hodnotu pro IT komunitu. Srdečně podporujeme open-source vývoj a usilujeme o jeho popularizaci, stejně jako spolupracujeme s předními univerzitami v Rusku v oblasti počítačových věd a matematiky.
Zajímavé úkoly soutěže a zapojení do nám milého projektu Telegram byly samy o sobě skvělou motivací, a navíc se stal cenový fond další stimulací. 🙂
Studie blockchainu TON
Pečlivě sledujeme nové trendy v blockchainu, umělé inteligenci a strojovém učení a snažíme se nevynechat žádné významné vydání v každé oblasti, ve které pracujeme. Proto byla naše tým již obeznámen s myšlenkami z . Nicméně před zahájením práce s TON jsme neanalyzovali technickou dokumentaci a skutečný zdrojový kód platformy, a proto byl první krok poměrně jasný — důkladné prozkoumání oficiální dokumentace na a v .
Na začátku soutěže byl kód již zveřejněn, a proto jsme se rozhodli ušetřit čas a hledat příručku nebo shrnutí napsané uživateli. Bohužel, to nepřineslo výsledky — kromě návodu na sestavení platformy na Ubuntu jsme nenašli žádné další materiály.
Samotná dokumentace se ukázala být pečlivě zpracovaná, ale v některých momentech ji bylo obtížné číst. Docela často jsme museli vracet k určitým bodům a přepínat se z vysoce úrovňových popisů abstraktních myšlenek na nízkoúrovňové detaily implementace.
Bylo by snadnější, kdyby ve specifikaci nebylo podrobné popisování implementace. Informace o tom, jak virtuální stroj představuje svůj zásobník, spíše odvádí pozornost vývojářům, kteří vytvářejí smart kontrakty pro platformu TON, než aby jim pomohly.
Nix: stavíme projekt
V Serokell jsme velkými fanoušky . Sestavujeme naše projekty pomocí , a na všech našich serverech je nainstalováno . Díky tomu jsou všechny naše buildy reprodukovatelné a fungují na jakémkoli operačním systému, na který lze Nix nainstalovat.
Proto jsme začali s vytvořením . Díky tomu je kompilace TON maximálně jednoduchá:
$ cd ~/.config/nixpkgs/overlays && git clone https://github.com/serokell/ton.nix
$ cd /path/to/ton/repo && nix-shell
[nix-shell]$ cmakeConfigurePhase && makeVšimněte si, že nemusíte instalovat žádné závislosti. Nix to udělá za vás, bez ohledu na to, zda používáte NixOS, Ubuntu nebo macOS.
Programování pro TON
Kód chytrých kontraktů v TON Network se vykonává na TON Virtual Machine (TVM). TVM je složitější než většina jiných virtuálních strojů a má velmi zajímavé funkce, například umí pracovat s pokračováními (continuations) a odkazy na data.
Kromě toho kluci z TON vytvořili hned tři nové programovací jazyky:
Fift — univerzální zásobníkový programovací jazyk, který připomíná . Jeho super schopnost — možnost interagovat s TVM.
FunC — programovací jazyk chytrých kontraktů, který je podobný a kompiluje se do dalšího jazyka — Fift Assembler.
Fift Assembler — knihovna Fift pro generování binárního spustitelného kódu pro TVM. Fift Assembler nemá kompilátor. Je to .
Naše soutěžní práce
Konečně přišel čas podívat se na výsledky našich snah.
Asynchronní platební kanál
Platební kanál (payment channel) — chytrý kontrakt, který umožňuje dvem uživatelům posílat platby mimo blockchain. Díky tomu se ušetří nejen peníze (žádné poplatky), ale i čas (nemusíte čekat, až se zpracuje další blok). Platby mohou být jakkoli malé a probíhat tak často, jak je to potřeba. Přitom si strany nemusí navzájem důvěřovat, protože spravedlnost konečného vyrovnání je zaručena chytrým kontraktem.
Našli jsme poměrně jednoduché řešení problému. Obě strany mohou vyměňovat podepsané zprávy, z nichž každá obsahuje dvě čísla — celkovou částku zaplacenou každým z účastníků. Tato dvě čísla fungují jako v tradičních distribuovaných systémech a určují pořadí "nastalo před" u transakcí. Pomocí těchto dat bude kontrakt schopen vyřešit jakýkoli možný konflikt.
Ve skutečnosti k realizaci této myšlenky stačí i jedno číslo, ale obě jsme si ponechali, protože nám to umožnilo vytvořit uživatelsky přívětivější rozhraní. Kromě toho jsme se rozhodli zahrnout do každé zprávy velikost platby. Bez tohoto údaje, pokud by zpráva z jakéhokoli důvodu ztratila, by si uživatel ztrátu nemusel všimnout, i když by všechny částky a konečné vyrovnání byly správné.
Abychom ověřili naši myšlenku, hledali jsme příklady použití tak jednoduchého a stručného protokolu platebního kanálu. K našemu překvapení jsme objevili pouze dva:
- podobného přístupu, pouze pro případ jednosměrného kanálu.
- , ve kterém je popsána stejná myšlenka jako u nás, jen bez vysvětlení mnoha důležitých detailů, jako je celková správnost a proces řešení konfliktů.
Bylo jasné, že má smysl podrobně popsat náš protokol, se zvláštní pozorností na jeho správnost. Po několika iteracích byla specifikace hotová a nyní si ji můžete také .
Implementovali jsme kontrakt v jazyce FunC a nástroj příkazového řádku pro interakci s naším kontraktem jsme zcela napsali v jazyce Fift, jak doporučovali organizátoři. Mohli jsme zvolit jakýkoli jiný jazyk pro náš CLI, ale chtěli jsme vyzkoušet právě Fift, abychom zjistili, jak se v praxi osvědčí.
Upřímně řečeno, po práci s Fift jsme nenašli žádné přesvědčivé důvody, proč dávat přednost tomuto jazyku před populárními a široce používanými jazyky s rozvinutým nástrojem a knihovnami. Programování ve stackovém jazyce je poměrně nepříjemné, protože musíte neustále mít na paměti, co kde leží ve stacku, a kompilátor s tím nepomáhá.
Proto je podle našeho názoru jediným ospravedlněním existence Fift jeho role jako hostitelského jazyka pro Assembler Fift. Nebylo by lepší integrovat assembler TVM do existujícího jazyka, než vymýšlet nový pro tento, v podstatě jediný, účel?
TVM Haskell eDSL
Nyní přišel čas hovořit o našem druhém chytrém kontraktu. Rozhodli jsme se vyvinout peněženku s více podpisy, ale psát další chytrý kontrakt v jazyce FunC by bylo příliš nudné. Chtěli jsme přidat nějakou zajímavost a tím se stal náš vlastní jazyk assembleru pro TVM.
Stejně jako Fift Assembler, náš nový jazyk je vložený, jen místo Fift jsme jako hostitele zvolili Haskell, což nám umožnilo plně využít jeho pokročilý typový systém. Při práci se smart kontrakty, kde může být cena i malé chyby velmi vysoká, se nám statická typizace jeví jako velká výhoda.
Abychom ukázali, jak vypadá assembler TVM zabudovaný do Haskellu, implementovali jsme na něm standardní peněženku. Tady je několik věcí, na které byste měli dávat pozor:
- Tento kontrakt se skládá z jedné funkce, ale můžete použít kolik jen chcete. Když definujete novou funkci v hostitelském jazyce (tedy v Haskellu), naše eDSL vám umožní vybrat si, zda chcete, aby se změnila na samostatnou podprogramu v TVM, nebo byla jednoduše vložena na místo volání.
- Stejně jako v Haskellu mají funkce typy, které se kontrolují během kompilace. V našem eDSL je typ vstupu funkce typ zásobníku, který funkce očekává, a typ výsledku je typ zásobníku, který vznikne po vyvolání.
- V kódu jsou anotace
stacktype, které popisují očekávaný typ zásobníku v místě volání. V původním kontraktu peněženky to byly pouze komentáře, ale v našem eDSL jsou vlastně součástí kódu a kontrolují se během kompilace. Mohou sloužit jako dokumentace nebo tvrzení, které pomáhají vývojáři najít problém v případě, že se při změně kódu změní typ zásobníku. Samozřejmě, že takové anotace nemají vliv na výkon během běhu, protože se pro ně žádný kód TVM negeneruje. - To je stále prototyp, napsaný za dva týdny, takže je před projektem ještě spousta práce. Například všechny instance tříd, které vidíte v níže uvedeném kódu, musí být generovány automaticky.
Takhle vypadá implementace multisig peněženky v našem eDSL:
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 @Word32Celý zdrojový kód našeho eDSL a smlouvy peněženky s vícero podpisy můžete najít v A podrobněji to Úvahy o soutěži a TON
Naše práce celkem trvala 380 hodin (včetně seznámení s dokumentací, schůzkami a samotným vývojem). Na soutěžním projektu se podílelo pět vývojářů: CTO, týmový vedoucí, specialisté na blockchainové platformy a vývojáři softwaru v Haskellu.
Celkem naše práce zabralo 380 hodin (včetně seznámení se s dokumentací, schůzkami a samotným vývojem). Na soutěžním projektu se podílelo pět vývojářů: CTO, týmový vedoucí, specialisté na blockchainové platformy a vývojáři softwaru v Haskellu.
Zdroje pro účast v soutěži jsme našli snadno, protože duch hackathonu, úzká týmová spolupráce a potřeba rychlého ponoření do aspektů nových technologií – to je vždy vzrušující. Několik neprospaných nocí kvůli dosažení maximálního výsledku v podmínkách omezených zdrojů je kompenzováno cennými zkušenostmi a skvělými vzpomínkami. Navíc práce na podobných úlohách je vždy dobrým testem procesů společnosti, protože dosáhnout skutečně důstojných výsledků bez výborně fungující vnitřní interakce je velmi obtížné.
Kromě lyriky: byli jsme ohromeni objemem práce, kterou tým TON odvedl. Podařilo se jim postavit složitý, krásný a co je nejdůležitější, funkční systém. TON se ukázal jako platforma s velkým potenciálem. Nicméně, aby se tento ekosystém rozvíjel, je třeba udělat ještě hodně, jak pokud jde o jeho využívání v blockchainových projektech, tak pokud jde o vylepšení vývojových nástrojů. Jsme hrdí, že jsme nyní součástí tohoto procesu.
Pokud máte po přečtení tohoto článku nějaké otázky nebo nápady, jak využít TON k řešení vašich úkolů, – rádi se podělíme o zkušenosti.
Zdroj: habr.com
