Probabil ați auzit acea Telegramă . Dar s-ar putea să fi ratat știrea că nu cu mult timp în urmă Telegramă pentru implementarea unuia sau mai multor contracte inteligente pentru această platformă.
Echipa Serokell, cu o vastă experiență în dezvoltarea de proiecte blockchain mari, nu a putut sta deoparte. Am delegat cinci angajați la competiție, iar două săptămâni mai târziu, aceștia au ocupat primul loc sub porecla aleatoare (in)modesta Sexy Chameleon. În acest articol voi vorbi despre cum au făcut-o. Sperăm că în următoarele zece minute vei citi măcar o poveste interesantă și cel mult vei găsi ceva util în ea pe care să-l aplici în munca ta.
Dar să începem cu puțin context.
Concurența și condițiile acesteia
Așadar, principalele sarcini ale participanților au fost implementarea unuia sau mai multor contracte inteligente propuse, precum și formularea de propuneri pentru îmbunătățirea ecosistemului TON. Concursul a avut loc în perioada 24 septembrie – 15 octombrie, iar rezultatele au fost anunțate abia pe 15 noiembrie. Destul de mult timp, avand in vedere ca in acest timp Telegram a reusit sa sustina si sa anunte rezultatele concursurilor de proiectare si dezvoltare de aplicatii in C++ pentru testarea si evaluarea calitatii apelurilor VoIP in Telegram.
Am selectat două contracte inteligente din lista propusă de organizatori. Pentru unul dintre ele, am folosit instrumente distribuite cu TON, iar al doilea a fost implementat într-un nou limbaj dezvoltat de inginerii noștri special pentru TON și integrat în Haskell.
Alegerea unui limbaj de programare funcțional nu este întâmplătoare. În a noastră Adesea vorbim despre motivul pentru care credem că complexitatea limbajelor funcționale este o uriașă exagerare și de ce le preferăm în general celor orientate pe obiecte. Apropo, conține și .
De ce ne-am hotărât să participăm?
Pe scurt, pentru că specializarea noastră este proiecte non-standard și complexe care necesită abilități speciale și sunt adesea de valoare științifică pentru comunitatea IT. Susținem cu tărie dezvoltarea open-source și suntem angajați în popularizarea acesteia și, de asemenea, cooperăm cu universități ruse de top în domeniul informaticii și matematicii.
Sarcinile interesante ale competiției și implicarea în îndrăgitul nostru proiect Telegram au fost în sine o motivație excelentă, dar fondul de premii a devenit un stimulent suplimentar. 🙂
Cercetare blockchain TON
Monitorizăm îndeaproape noile evoluții în blockchain, inteligența artificială și învățarea automată și încercăm să nu pierdem o singură versiune semnificativă în fiecare dintre domeniile în care lucrăm. Prin urmare, până la începutul competiției, echipa noastră era deja familiarizată cu ideile de la . Cu toate acestea, înainte de a începe lucrul cu TON, nu am analizat documentația tehnică și codul sursă real al platformei, așa că primul pas a fost destul de evident - un studiu amănunțit al documentației oficiale privind și .
Până la începutul competiției, codul fusese deja publicat, așa că pentru a economisi timp, am decis să căutăm un ghid sau un rezumat scris de utilizatorii. Din păcate, acest lucru nu a dat niciun rezultat - în afară de instrucțiunile de asamblare a platformei pe Ubuntu, nu am găsit alte materiale.
Documentația în sine a fost bine cercetată, dar a fost dificil de citit în unele zone. Destul de des a trebuit să ne întoarcem la anumite puncte și să trecem de la descrieri de nivel înalt ale ideilor abstracte la detalii de implementare de nivel scăzut.
Ar fi mai ușor dacă specificația nu ar include deloc o descriere detaliată a implementării. Informațiile despre modul în care o mașină virtuală își reprezintă stiva este mai probabil să distragă atenția dezvoltatorilor care creează contracte inteligente pentru platforma TON decât să îi ajute.
Nix: realizarea proiectului
La Serokell suntem mari fani . Colectăm proiectele noastre cu el și le implementăm folosind , și instalat pe toate serverele noastre . Datorită acestui fapt, toate versiunile noastre sunt reproductibile și funcționează pe orice sistem de operare pe care poate fi instalat Nix.
Așa că am început prin a crea . Cu ajutorul lui, compilarea TON este cât se poate de simplă:
$ cd ~/.config/nixpkgs/overlays && git clone https://github.com/serokell/ton.nix
$ cd /path/to/ton/repo && nix-shell
[nix-shell]$ cmakeConfigurePhase && makeRețineți că nu trebuie să instalați nicio dependență. Nix va face totul pentru tine, indiferent dacă folosești NixOS, Ubuntu sau macOS.
Programare pentru TON
Codul de contract inteligent din rețeaua TON rulează pe mașina virtuală TON (TVM). TVM este mai complex decât majoritatea altor mașini virtuale și are o funcționalitate foarte interesantă, de exemplu, poate funcționa cu continuări и link-uri către date.
Mai mult, băieții de la TON au creat trei noi limbaje de programare:
Cinci este un limbaj de programare universal care seamănă cu . Super-abilitatea lui este abilitatea de a interacționa cu TVM.
FunC este un limbaj de programare smart contract care este similar cu și este compilat într-o altă limbă - Fift Assembler.
Al cincilea asamblator — Cinci biblioteci pentru generarea de cod binar executabil pentru TVM. Fifth Assembler nu are un compilator. Acest .
Concurența noastră funcționează
În sfârșit, este timpul să ne uităm la rezultatele eforturilor noastre.
Canal de plată asincron
Canalul de plată este un contract inteligent care permite doi utilizatori să trimită plăți în afara blockchain-ului. Ca rezultat, economisiți nu numai bani (nu există comision), ci și timp (nu trebuie să așteptați ca următorul bloc să fie procesat). Plățile pot fi cât se dorește și cât de des este necesar. În acest caz, părțile nu trebuie să aibă încredere una în alta, deoarece corectitudinea decontării finale este garantată de contractul inteligent.
Am găsit o soluție destul de simplă la problemă. Două părți pot face schimb de mesaje semnate, fiecare conținând două numere - suma totală plătită de fiecare parte. Aceste două numere funcționează ca în sistemele tradiționale distribuite și setați ordinea „sa întâmplat înainte” asupra tranzacțiilor. Folosind aceste date, contractul va putea rezolva orice posibil conflict.
De fapt, un număr este suficient pentru a implementa această idee, dar le-am lăsat pe amândouă pentru că astfel am putea face o interfață de utilizator mai convenabilă. În plus, am decis să includem suma plății în fiecare mesaj. Fără el, dacă mesajul este pierdut dintr-un motiv oarecare, atunci, deși toate sumele și calculul final vor fi corecte, utilizatorul poate să nu observe pierderea.
Pentru a ne testa ideea, am căutat exemple de utilizare a unui protocol de plată atât de simplu și concis. În mod surprinzător, am găsit doar două:
- o abordare similară, doar în cazul unui canal unidirecțional.
- , care descrie aceeași idee ca a noastră, dar fără a explica multe detalii importante, precum corectitudinea generală și procedurile de soluționare a conflictelor.
A devenit clar că are sens să descriem protocolul nostru în detaliu, acordând o atenție deosebită corectitudinii sale. După mai multe iterații, specificația era gata, iar acum poți și tu. .
Am implementat contractul în FunC și am scris utilitarul de linie de comandă pentru a interacționa cu contractul nostru în întregime în Fift, așa cum este recomandat de organizatori. Am fi putut alege orice altă limbă pentru CLI-ul nostru, dar am fost interesați să încercăm Fit pentru a vedea cum a funcționat în practică.
Sincer să fiu, după ce am lucrat cu Fift, nu am văzut niciun motiv convingător pentru a prefera acest limbaj limbilor populare și utilizate în mod activ, cu instrumente și biblioteci dezvoltate. Programarea într-un limbaj bazat pe stivă este destul de neplăcută, deoarece trebuie să păstrați în mod constant în cap ceea ce este pe stivă, iar compilatorul nu ajută la asta.
Prin urmare, în opinia noastră, singura justificare a existenței Fift este rolul său de limbă gazdă pentru Fift Assembler. Dar nu ar fi mai bine să încorporăm asamblatorul TVM într-un limbaj existent, decât să inventăm unul nou pentru acest singur scop?
TVM Haskell eDSL
Acum este timpul să vorbim despre al doilea contract inteligent. Am decis să dezvoltăm un portofel cu semnături multiple, dar să scriem un alt contract inteligent în FunC ar fi prea plictisitor. Am vrut să adăugăm ceva aromă și acesta a fost propriul nostru limbaj de asamblare pentru TVM.
La fel ca Fift Assembler, noul nostru limbaj este încorporat, dar am ales Haskell ca gazdă în loc de Fift, permițându-ne să profităm din plin de sistemul său de tip avansat. Când lucrați cu contracte inteligente, unde costul chiar și al unei mici erori poate fi foarte mare, tastarea statică, în opinia noastră, este un mare avantaj.
Pentru a demonstra cum arată asamblatorul TVM încorporat în Haskell, am implementat un portofel standard pe acesta. Iată câteva lucruri la care să acordați atenție:
- Acest contract constă dintr-o singură funcție, dar puteți folosi câte doriți. Când definiți o nouă funcție în limba gazdă (adică Haskell), eDSL-ul nostru vă permite să alegeți dacă doriți să devină o rutină separată în TVM sau pur și simplu integrată la punctul de apel.
- Ca și Haskell, funcțiile au tipuri care sunt verificate în timpul compilării. În eDSL-ul nostru, tipul de intrare al unei funcții este tipul de stivă pe care funcția îl așteaptă, iar tipul de rezultat este tipul de stivă care va fi produs după apel.
- Codul are adnotări
stacktype, care descrie tipul de stivă așteptat la punctul de apel. În contractul original de portofel, acestea erau doar comentarii, dar în eDSL-ul nostru, acestea fac de fapt parte din cod și sunt verificate în timpul compilării. Ele pot servi ca documentație sau declarații care ajută dezvoltatorul să găsească problema dacă codul se modifică și tipul stivei se modifică. Desigur, astfel de adnotări nu afectează performanța de rulare, deoarece nu este generat niciun cod TVM pentru ele. - Acesta este încă un prototip scris în două săptămâni, așa că mai este mult de lucru la proiect. De exemplu, toate instanțele claselor pe care le vedeți în codul de mai jos ar trebui să fie generate automat.
Iată cum arată implementarea unui portofel multisig pe eDSL-ul nostru:
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 @Word32Codul sursă complet al contractului nostru eDSL și portofel cu semnături multiple poate fi găsit la Și altele despre limbile încorporate, colegul nostru Georgy Agapov.
Concluzii despre competiție și TON
În total, munca noastră a durat 380 de ore (inclusiv familiarizarea cu documentația, întâlnirile și dezvoltarea efectivă). La proiectul competiției au participat cinci dezvoltatori: CTO, team leader, specialiști în platforme blockchain și dezvoltatori de software Haskell.
Am găsit resurse pentru a participa la concurs fără dificultate, deoarece spiritul unui hackathon, munca în echipă strânsă și nevoia de a ne cufunda rapid în aspectele noilor tehnologii sunt mereu incitante. Câteva nopți nedormite pentru a obține rezultate maxime în condiții de resurse limitate sunt compensate de experiență neprețuită și amintiri excelente. În plus, lucrul la astfel de sarcini este întotdeauna un bun test al proceselor companiei, deoarece este extrem de dificil să obții rezultate cu adevărat decente fără o interacțiune internă funcțională.
Versuri deoparte: am fost impresionați de cantitatea de muncă depusă de echipa TON. Au reușit să construiască un sistem complex, frumos și, cel mai important, funcțional. TON s-a dovedit a fi o platformă cu un mare potențial. Cu toate acestea, pentru ca acest ecosistem să se dezvolte, trebuie făcut mult mai mult, atât în ceea ce privește utilizarea lui în proiecte blockchain, cât și în ceea ce privește îmbunătățirea instrumentelor de dezvoltare. Suntem mândri că acum facem parte din acest proces.
Dacă după ce ați citit acest articol mai aveți întrebări sau aveți idei despre cum să utilizați TON pentru a vă rezolva problemele, — vom fi bucuroși să împărtășim experiența noastră.
Sursa: www.habr.com
