Muutes FunC FunCtionaliks Haskelliga: kuidas Serokell vÔitis Telegrami Blockchaini konkurssi

Te olete kindlasti kuulnud, et Telegram kavatseb kĂ€ivitada plokiahela platvormi Ton. Kuid te vĂ”isite jÀÀda ilma uudisest, et mitte nii kaua tagasi Telegram kuulutas vĂ€lja konkursi ĂŒhe vĂ”i mitme nutilepingute elluviimiseks sellel platvormil.

Serokelli meeskond, kellel on ulatuslik kogemus suurte plokiahela projektide arendamisel, ei saanud kĂ”rvale jÀÀda. Me delegaatsime konkursile viis töötajat, ja juba kahe nĂ€dala pĂ€rast saavutasid nad seal esikoha (mitte)vaikselt tegutseva kasutajanimega Sexy Chameleon. Selles artiklis rÀÀgin, kuidas nad selle saavutada suutsid. Loodame, et jĂ€rgmise kĂŒmne minuti jooksul loete vĂ€hemalt huvitava loo, ja ehk leiate selles midagi kasulikku, mida oma töös kasutada.

Aga alustame vÀikese konteksti tÔlgendamisega.

Konkursi tingimused

Nii et osalejate peamised ĂŒlesanded olid ĂŒhe vĂ”i mitme pakutud nutilepingu elluviimine ning ettepanekute tegemine TONi ökosĂŒsteemi tĂ€iustamiseks. Konkurs kestis 24. septembrist kuni 15. oktoobrini, kuid tulemused kuulutati vĂ€lja alles 15. novembril. Suhteliselt pikk aeg, arvestades, et selle aja jooksul suutis Telegram korraldada ja vĂ€lja kuulutada disainikonkursside ja C++ rakenduste arenduskonkursi tulemused VoIP kĂ”nede testimise ja kvaliteedi hindamiseks Telegramis.

Valisime korraldajate pakutud nimekirjast vĂ€lja kaks nutilepingut. Ühe jaoks kasutasime TONiga kaasasolevaid tööriistu, teise rakendasime uuel keelel, mille meie insenerid koostasid spetsiaalselt TONi jaoks ja mis on integreeritud Haskellisse.

Funktsionaalse programmeerimiskeele valik ei ole juhuslik. Meie ettevĂ”tte blogis rÀÀgime sageli, miks peame funktsionaalsete keelte keerukust suureks liialduseks ja miks eelistame neid ĂŒldiselt objekti-orienteeritud keeltele. Muide, seal on ka artikli originaal.

Miks me ĂŒldse otsustasime osaleda

LĂŒhidalt öeldes, meie spetsialiseerumine on ebatavalised ja keerulised projektid, mis nĂ”uavad erioskusi ja omavad sageli teaduslikku vÀÀrtust IT-kommuunile. Me toetame innukalt open-source arendust ja tegeleme selle populariseerimisega ning teeme koostööd Venemaa juhtivate ĂŒlikoolidega arvutiteaduse ja matemaatika valdkonnas.

VĂ”istluse huvitavad ĂŒlesanded ja seotus meie armastatud projektiga Telegram olid iseenesest suurepĂ€rane motivatsioon, lisaks tĂ”i auhinnafond tĂ€iendavat stiimulit. 🙂

TONi plokiahela uurimine

Me jĂ€lgime tĂ€helepanelikult uusi arenguid plokiahelas, tehisintellektis ja masinĂ”ppes, pĂŒĂŒdes mitte magada maha ĂŒhtegi olulist vĂ€ljaannet igas valdkonnas, kus töötame. Seega, konkursi alguseks oli meie meeskond juba tuttav ideedega, mis on pĂ€rit TON white paperist. Enne kui alustasime tööga TONi kallal, ei analĂŒĂŒsinud me tehnilist dokumentatsiooni ega platvormi tegelikku lĂ€htekoodi, seega esimene samm oli ĂŒsna selge — pĂ”hjalik uurimine ametlikust dokumentatsioonist veebisaidil ja projekti hoidlas.

Kuna konkursi alguseks oli kood juba avaldatud, otsustasime aega sÀÀstes otsida juhendit vĂ”i kokkuvĂ”tet, mille olid kirjutanud kasutajad. Kahjuks ei andnud see tulemust — peale juhiste Ubuntu platvormi ĂŒlesehitamiseks ei leidnud me muid materjale.

Dokumentatsioon ise osutus hĂ€sti lĂ€bimĂ”eldud, kuid selle lugemine oli mĂ”ningates kohtades keeruline. Me pidime ĂŒsna sageli tagasi pöörduma erinevate punktide juurde ja liikuma kĂ”rgetasemelistelt abstraktsetelt ideedelt madalama taseme rakenduse detailide juurde.

Oleks olnud lihtsam, kui spetsifikatsioonis ei oleks ĂŒldse olnud ĂŒksikasjalikku rakenduse kirjeldust. Teave selle kohta, kuidas virtuaalne masin oma virna esitleb, pigem segab arendajaid, kes loovad TONi platvormile nutikaid lepinguid, kui aitab neid.

Nix: kogume projekti

Serokellis oleme suured fĂ€nnid Nix. Me kogume oma projekte ja kĂ€ivitame need kasutades NixOps, ja kĂ”ikidel meie serveritel on paigaldatud NixOS. TĂ€nu sellele on kĂ”ik meie buildid reprodukutitavad ja töötavad igas operatsioonisĂŒsteemis, kuhu Nixi saab installida.

Seega alustasime Nixi overlay loomisest TONi kogumise vÀljendiga. Tema abil on TON-i kiiresti kompileerida:

$ cd ~/.config/nixpkgs/overlays && git clone https://github.com/serokell/ton.nix
$ cd /path/to/ton/repo && nix-shell
[nix-shell]$ cmakeConfigurePhase && make

Pange tÀhele, et te ei pea installima mingeid sÔltuvusi. Nix teeb kÔik teie eest maagiliselt olenemata sellest, kas kasutate NixOS-i, Ubuntu vÔi macOS-i.

Programmeerimine TON-i jaoks

Tarkvaralepingud TON vÔrgus töötavad TON Virtuaalmasinas (TVM). TVM on keerulisem kui enamik teisi virtuaalmasinaid ja omab vÀga huvitavaid funktsioone, nÀiteks oskab ta töötada katkestustega (continuations) ja andmeviidete (data references).

Veelgi enam, TON-i poisid on loonud lausa kolm uut programmeerimiskeelt:

Fift — universaalne virnastatud programmeerimiskeel, mis meenutab Forth. Tema supervĂ”ime on suhelda TVM-iga.

FunC — tarkvaralepingute programmeerimiskeel, mis sarnaneb C ja kompileeritakse veel ĂŒheks keeleks — Fift Assembliks.

Fift Assembler — Fifti teek TVM-i jaoks binaarse tĂ€idetava koodi genereerimiseks. Fift Assemblil pole kompilaatorit. See on sisseehitatud domaani spetsiifiline keel (eDSL).

Meie konkursitööd

LÔpuks on aeg vaadata meie pingutuste tulemusi.

AsĂŒnkroonne maksetee

Maksetee (payment channel) — tarkvaraleping, mis vĂ”imaldab kahel kasutajal teha makseid vĂ€ljaspool plokiahelat. SeelĂ€bi sÀÀstetakse mitte ainult raha (komisjoni pole), vaid ka aega (te ei pea ootama, kuni jĂ€rgmine plokk töödeldakse). Maksed vĂ”ivad olla tootmiselt nii vĂ€ikesed kui vaja ja toimuda niipea kui vajalik. Samuti ei pea pooled ĂŒksteisega usaldama, kuna lĂ”pliku arveldamise Ă”iglus on tagatud tarkvaralepinguga.

Me leidsime probleemi jaoks ĂŒsna lihtsa lahenduse. Kaks osalist saavad vahetada allkirjastatud sĂ”numeid, millest igaĂŒks sisaldab kahte numbrit — iga osaleja makstud kogusummat. Need kaks numbrit toimivad nagu vektorikellad traditsioonilistes hajusates sĂŒsteemides ja mÀÀratlevad jĂ€rjekorra "toimus enne" tehingute puhul. Kasutades neid andmeid, suudab leping lahendada iga vĂ”imaliku konflikti.

Tegelikult piisab selle idee elluviimiseks ĂŒhest numbrist, kuid jĂ€tsime mĂ”lemad, kuna nii suudame teha mugavama kasutajaliidese. Lisaks otsustasime igas sĂ”numis nĂ€idata makse suurust. Ilma selleta, kui sĂ”num mingil pĂ”hjusel kaob, vĂ”ib kasutaja kaotust mitte mĂ€rgata, kuigi kĂ”ik summad ja lĂ”plik arvestus on Ă”iged.

Kuna tahtsime meie ideed testida, otsisime nĂ€iteid sellise lihtsa ja lakoonilise maksekanali protokolli kasutamisest. Üllatuseks leidsime vaid kaks:

  1. Kirjeldus sarnast lĂ€henemist, ainult ĂŒhe suunalise kanali puhul.
  2. Tutorial, milles on kirjeldatud sama ideed, mis meil, kuid ilma selgitusteta paljude oluliste detailide kohta, nagu ĂŒldine kehtivus ja konfliktide lahendamise protseduur.

Selgeks sai, et on mĂ”istlik detailselt kirjeldada meie protokolli, pöörates erilist tĂ€helepanu selle kehtivusele. PĂ€rast mitmeid ringe oli spetsifikatsioon valmis ja nĂŒĂŒd saate seda ka vaadata.

Rakendasime lepingu FunC-is ning CLI tööriista meie lepinguga suhtlemiseks kirjutasime tÀielikult Fiftis, nagu soovitasid korraldajad. Me oleksime saanud valida mÔne muu keele meie CLI jaoks, kuid meid huvitab proovida just Fiftit, et nÀha, kuidas see praktikas toimib.

Ausalt öeldes, pĂ€rast Fiftiga töötamist ei nĂ€inud me tĂ”siseid pĂ”hjuseid eelistada seda keelt populaarsetele ja laialdaselt kasutatavatele keeltele, millel on hĂ€sti arenenud tööriistad ja raamatukogud. Steke keeles programmeerimine on ĂŒsna ebameeldiv, kuna tuleb pidevalt meeles pidada, kus miski virnas asub, ja kompilaator selles ei aita.

Seega on ainus meie arvates Fift olemasolu Ôigustus selle roll host-keelena Fift Assembler'i jaoks. Kas poleks parem, kui integreerida TVM assembler mÔne olemasoleva keele sisse, selle asemel et vÀlja mÔelda uus just selle, sisuliselt ainulaadse, eesmÀrgi jaoks?

TVM Haskell eDSL

NĂŒĂŒd on aeg rÀÀkida meie teisest nutilepingust. Otsustasime arendada multifirma rahakoti, kuid uue nutilepingu kirjutamine FunC-is oleks olnud liiga igav. Soovisime lisada midagi erilist, ja selleks valisime meie enda assembleri keele TVM jaoks.

Nagu Fift Assembler, on meie uus keel sisseembedatud, kuid hostina valisime Haskelli, mis vĂ”imaldab meil tĂ€ielikult kasutada selle edasijĂ”udnud tĂŒĂŒbistikut. Targa lepingu puhul, kus isegi vĂ€ikse vea hind vĂ”ib olla vĂ€ga kĂ”rge, on staatiline tĂŒĂŒbihindamine meie arvates suur eelis.

Kuidas demonstreerida, kuidas Haskellisse sisseembedatud TVM assembler vÀlja nÀeb, oleme selle peal rakendanud tavalise rahakoti. Siin on mÔned asjad, millele tasub tÀhelepanu pöörata:

  • See leping koosneb ĂŒhest funktsioonist, kuid vĂ”ite kasutada nii palju, kui soovite. Kui teete hostkeeles (st Haskellis) uue funktsiooni, vĂ”imaldab meie eDSL teil valida, kas soovite, et see muutuks eraldi alamprogrammi TVMs vĂ”i lihtsalt integreeritakse kutsumisekohta.
  • Nagu Haskellis, on funktsioonidel tĂŒĂŒbid, mis kontrollitakse kompileerimise ajal. Meie eDSL-is on funktsiooni sisendi tĂŒĂŒp see, mida funktsioon vĂ€ntab, ja tulemuste tĂŒĂŒp on see, mis tekib pĂ€rast kutset.
  • Koodis on annotatsioonid stacktype, mis kirjeldavad oodatavat tĂŒĂŒbistikut kutsepunktil. Originaalses rahakoti lepingus olid need lihtsalt kommentaarid, kuid meie eDSL-is on need tegelikult osa koodist ja neid kontrollitakse kompileerimise ajal. Need vĂ”ivad toimida dokumentatsioonina vĂ”i kinnitustena, mis aitavad arendajal probleemile jĂ€lile jĂ”uda juhuks, kui koodi muutmisel tĂŒĂŒbistik muutub. Muidugi ei mĂ”juta sellised annotatsioonid tööajal tulemuslikkust, kuna nende jaoks ei genereerita ĂŒhtegi TVM koodi.
  • See on endiselt prototĂŒĂŒp, mis kirjutati kahe nĂ€dalaga, seega on projektis veel palju tööd teha. NĂ€iteks kĂ”ik klasside eksemplarid, mida nĂ€ete allpool olevas koodis, peavad olema automaatselt genereeritud.

Nii nÀeb vÀlja multisig-rahakoti rakendamine meie eDSL-is:

peamine :: IO ()
peamine = putText $ pretty $ declProgram protsessid meetodid
  kus
    protsessid =
      [ ("recv_external", decl recvExternal)
      , ("recv_internal", decl recvInternal)
      ]
    meetodid =
      [ ("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
  tuletab (Eq, Ord, Show, Generic)

instance Exception WalletError

instance Enum WalletError where
  toEnum 33 = SeqNoMismatch
  toEnum 34 = SignatureMismatch
  toEnum _ = error "Tundmatu 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 @Word32

Teie saate tĂ€ielikku algkoodi meie eDSL ja mult_signature rahakoti lepingu leida selles hoidlas. Ja rohkem rÀÀkis ĂŒksikasjalikult meie kolleeg Georgy Agapov.

KokkuvÔtted konkursist ja TON

Kokku meie töö vÔttis 380 tundi (koos dokumentatsiooniga tutvumise, koosolekute ja arendamisega). Konkursi projektis osales viis arendajat: CTO, meeskonna juht, plokiahela platvormide spetsialistid ja Haskellil tarkvara arendajad.

Meeskonnale osalemiseks vajalikke ressursse leidsime kiiresti, kuna hackathoni vaim, tihe meeskonnatöö, vajadus uute tehnoloogiate aspektidesse kiiresti sĂŒveneda — see on alati haarav. Mitmed unetukesed maksimaalse tulemuse nimel piiratud ressursside tingimustes on tasustatud hindamatu kogemuse ja suurepĂ€raste mĂ€lestustega. Lisaks on selliste ĂŒlesannete kallal töötamine alati suurepĂ€rane katsumus ettevĂ”tte protsesside jaoks, kuna tĂ”eliselt rahuldavate tulemuste saavutamine ilma hĂ€sti toimiva sisemise koostööta on ÀÀrmiselt keeruline.

Lisaks lirikale: olime muljetavaldatud TONi meeskonna tehtud töö mahust. Neil Ă”nnestus luua keeruline, ilus ja mis kĂ”ige tĂ€htsam, töötav sĂŒsteem. TON on nĂ€idanud end platvormina, millel on suur potentsiaal. Kuid selleks, et ökosĂŒsteem areneks, tuleb veel palju teha, nii plokiahela projektides kasutamise osas kui ka arendustööriistade tĂ€iustamise osas. Oleme uhked, et oleme nĂŒĂŒd osa sellest protsessist.

Kui pĂ€rast selle artikli lugemist on teil veel kĂŒsimusi vĂ”i olete leidnud ideid, kuidas TONi oma probleemide lahendamiseks rakendada, kirjutage meile — me jagame meeleldi oma kogemusi.

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster