Duke e shndërruar FunC në FunCtional me Haskell: si Serokell fitoi në Garën e Blockchain të Telegramit

Ju sigurisht keni dëgjuar se Telegram po planifikon të lansojë platformën blockchain Ton. Por mund të keni humbur lajmin se së fundmi Telegram shpalli një konkurs për realizimin e një ose më shumë kontratave smart për këtë platformë.

Ekipi i Serokell me përvojë të pasur në zhvillimin e projekteve të mëdha blockchain nuk mund të qëndronte indiferent. Ne deleguam pesë punonjës në konkurs, dhe vetëm pas dy javësh ata zënë vendin e parë nën (jo)modestin pseudonim Sexy Chameleon. Në këtë artikull do të flas për mënyrën se si ia dolën. Shpresojmë që gjatë dhjetë minutave të ardhshme të lexoni një histori interesante dhe ndoshta të gjeni diçka të dobishme, që mund ta aplikoni në punën tuaj.

Por le të fillojmë me një përmbledhje të shkurtër të kontekstit.

Konkursi dhe kushtet e tij

Pra, detyrat kryesore të pjesëmarrësve ishin realizimi i një ose më shumë nga kontratat smart të propozuara, si dhe ofrimi i sugjerimeve për përmirësimin e ekosistemit TON. Konkursi u zhvillua nga 24 Shtatori deri më 15 Tetor, dhe rezultatet u shpallën vetëm më 15 Nëntor. Mjaft vonë, duke marrë parasysh se në këtë kohë Telegram kishte kryer dhe shpallur rezultatet e konkurseve për dizajn dhe për zhvillimin e aplikacioneve në C++ për testimin dhe vlerësimin e cilësisë së thirrjeve VoIP në Telegram.

Ne zgjodhëm nga lista e propozuar nga organizatorët dy kontrata smart. Për njërën prej tyre përdorëm mjetet që shpërndahen me TON, ndërsa tjetrën e realizuam në një gjuhë të re, të zhvilluar nga inxhinierët tanë specifikisht për TON dhe të integruar në Haskell.

Zgjedhja e një gjuhe programimi funksionale nuk është rastësi. Në blogun tonë korporativ ne flasim shpesh përse e konsiderojmë kompleksitetin e gjuhëve funksionale si një teprim të madh dhe përse në përgjithësi i preferojmë ato të orientuara në objekt. Për më tepër, aty është edhe origjinali i këtij artikulli.

Pse vendosëm të merrnim pjesë

Në përmbledhje, sepse specializimi ynë është në projekte jo standarde dhe të ndërlikuara që kërkojnë aftësi të veçanta dhe shpesh paraqesin vlerë shkencore për komunitetin IT. Ne mbështesim me zjarr zhvillimin open-source dhe angazhohemi për popullarizimin e tij, si dhe bashkëpunojmë me universitetet kryesore të Rusisë në fushën e shkencave kompjuterike dhe matematikës.

Detyrat interesante tĂ« konkursit dhe pĂ«rfshirja nĂ« njĂ« projekt qĂ« e duam ka qenĂ« njĂ« motivim i shkĂ«lqyer, ndĂ«rsa fondi i çmimeve ishte njĂ« stimul shtesĂ«. 🙂

Kërkimi i blockchain-it TON

Ne ndjekim me vijimĂ«si zhvillimet e reja nĂ« blockchain, inteligjencĂ«n artificiale dhe mĂ«simin e makinerive dhe pĂ«rpiqemi tĂ« mos humbasim asnjĂ« lançim tĂ« rĂ«ndĂ«sishĂ«m nĂ« çdo fushĂ« ku punojmĂ«. Prandaj, nĂ« momentin e fillimit tĂ« konkursit ekipi ynĂ« ishte tashmĂ« i njohur me idetĂ« nga TON white paper. MegjithatĂ«, pĂ«rpara fillimit tĂ« punĂ«s me TON nuk kishim analizuar dokumentacionin teknik dhe kodin origjinal tĂ« platformĂ«s, kĂ«shtu qĂ« hapi i parĂ« ishte mjaft i qartĂ« — njĂ« kĂ«rkim i kujdesshĂ«m i dokumentacionit zyrtar nĂ« tĂ« internetit dhe nĂ« repozitorin e projektit.

NĂ« fillim tĂ« konkursit kodi ishte tashmĂ« i publikuar, kĂ«shtu qĂ«, pĂ«r tĂ« kursyer kohĂ«, ne vendosĂ«m tĂ« kĂ«rkojmĂ« njĂ« udhĂ«zues ose njĂ« pĂ«rmbledhje tĂ« shkruar nga pĂ«rdoruesit. FatkeqĂ«sisht, kjo nuk dha rezultate — pĂ«rveç udhĂ«zimeve pĂ«r ndĂ«rtimin e platformĂ«s nĂ« Ubuntu, nuk gjetĂ«m materiale tĂ« tjera.

Dokumentacioni i vetë ka qenë i punuar me kujdes, por leximi i tij në disa momente ishte i vështirë. Shpesh na duhej të ktheheshim në pika të caktuara dhe të kalonim nga përshkrimet e nivelit të lartë të ideve abstrakte në detaje të nivelit të ulët të realizimit.

Do të ishte më e lehtë nëse në specifikim nuk do të kishte përshkrime të detajuara të realizimit. Informacioni se si makina virtuale paraqet stekimin e saj, më shumë e shpërqendron zhvilluesit që krijojnë kontrata smart për platformën TON, sesa e ndihmon ata.

Nix: ndërtimi i projektit

Në Serokell ne jemi adhurues të mëdhenj Nix. Ne e ndërtuam projektet tona dhe i implementojmë me ndihmën e NixOps, dhe të gjithë serverët tanë kanë të instaluar NixOS. Falë kësaj, të gjitha ndërtimet tona janë të riprodhueshme dhe funksionojnë nën çdo sistem operativ mbi të cilin mund të instaloni Nix.

Prandaj, ne filluam me krijimin e Nix overlay me një shprehje për ndërtimin e TON. Me ndihmën e tij, të kompilosh TON është maksimalisht e thjeshtë:

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

Vini re, nuk keni nevojë të instaloni asnjë varësi. Nix e bën mrekullinë për ju pavarësisht nëse po përdorni NixOS, Ubuntu apo macOS.

Programimi për TON

Kodi i kontraktit të mençur në TON Network ekzekutohet në TON Virtual Machine (TVM). TVM është më komplekse se shumica e makinave të tjera virtuale dhe ka shumë funksionalitete interesante, për shembull, ajo mund të punojë me vazhdimet (continuations) dhe lidhjet me të dhëna.

Më tepër, ekipi i TON krijoi tre gjuhë të reja programimi:

Fift — njĂ« gjuhĂ« programimi universale e tipit stack, e cila ngjan me Forth. Superfuqia e saj Ă«shtĂ« mundĂ«sia pĂ«r tĂ« interaguar me TVM.

FunC — njĂ« gjuhĂ« programimi pĂ«r kontraktet e mençura, e cila Ă«shtĂ« e ngjashme me C dhe kompilon nĂ« njĂ« gjuhĂ« tjetĂ«r — Fift Assembler.

Fift Assembler — njĂ« bibliotekĂ« Fift pĂ«r gjenerimin e kodit ekzekutiv binar pĂ«r TVM. Fift Assembler nuk ka kompjilator. Ky Ă«shtĂ« njĂ« gjuhĂ« e integruar e orientuar nga objekti (eDSL).

Këto janë punët tona konkurruese

Më në fund, është koha për të parë rezultatet e përpjekjeve tona.

Kanal pagesash i asinkronizuar

Kanal pagesash (payment channel) — Ă«shtĂ« njĂ« kontrakt inteligjent qĂ« lejon dy pĂ«rdorues tĂ« dĂ«rgojnĂ« pagesa jashtĂ« blockchain. Si rezultat, jo vetĂ«m qĂ« kursehen para (nuk ka komision), por edhe koha (nuk Ă«shtĂ« e nevojshme tĂ« presĂ«sh pĂ«r pĂ«rpunimin e bllokut tĂ« ardhshĂ«m). Pagesat mund tĂ« jenĂ« sa mĂ« tĂ« vogla dhe tĂ« ndodhin sa herĂ« qĂ« Ă«shtĂ« e nevojshme. NĂ« tĂ« njĂ«jtĂ«n kohĂ«, palĂ«t nuk duhet tĂ« besojnĂ« njĂ«ra-tjetrĂ«n, pasi drejtĂ«sia e llogaritjes pĂ«rfundimtare garanton nga kontrakti inteligjent.

Ne gjetĂ«m njĂ« zgjidhje mjaft tĂ« thjeshtĂ« pĂ«r problematikĂ«n. TĂ« dy palĂ«t mund tĂ« ndajnĂ« mesazhe tĂ« nĂ«nshkruara, secila prej tĂ« cilave pĂ«rmban dy numra — shumĂ«n totale tĂ« paguar nga secili nga pjesĂ«marrĂ«sit. KĂ«ta dy numra veprojnĂ« si orĂ« vektoriale nĂ« sistemet tradicionale tĂ« shpĂ«rndara dhe pĂ«rcaktojnĂ« rendin "ka ndodhur para" nĂ« transaksione. Duke pĂ«rdorur kĂ«to tĂ« dhĂ«na, kontrakti mund tĂ« zgjidhĂ« çdo konflikt tĂ« mundshĂ«m.

Në të vërtetë, për realizimin e kësaj ideje mjafton një numër, por ne e lanë të dy, sepse kështu arritëm të krijojmë një ndërfaqe përdoruesi më të përshtatshme. Përveç kësaj, vendosëm të përfshimë në çdo mesazh madhësinë e pagesës. Pa të, nëse mesazhi humbet për ndonjë arsye, atëherë megjithëse të gjitha shumat dhe llogaritja përfundimtare do të ishin të sakta, përdoruesi mund të mos e vërë re humbjen.

Për të testuar idenë tonë, kërkuam shembuj të përdorimit të një protokolli të tillë të thjeshtë dhe të qartë të kanalit të pagesave. Për t'u befasuar, gjetëm vetëm dy:

  1. Përshkrimi një qasje të ngjashme, vetëm për një kanal njëdrejtimor.
  2. Tutorial, i cili përshkruan të njëjtën ide si ne, vetëm pa shpjegimin e shumë detajeve të rëndësishme, si saktësia e përgjithshme dhe procedura e zgjidhjes së konflikteve.

U bë e qartë se kishte kuptim të përshkruanim protokollin tonë në detaje, duke i kushtuar vëmendje të veçantë saktësisë së tij. Pas disa iteracioneve, specifikimi ishte gati, dhe tani edhe ju mund të shihni atë.

Ne implementuam kontratën në FunC, dhe utilitarin e komandës për të ndërvepruar me kontratën tonë e shkruam plotësisht në Fift, ashtu siç rekomandonin organizatorët. Mund të kishim zgjedhur çdo gjuhë tjetër për CLI-së tonë, por ishte interesante të provonim pikërisht Fift, për të parë se si do të sillte në praktikë.

Me sinqeritet, pasi punuam me Fift, nuk pamë arsye të forta për të preferuar këtë gjuhë ndaj gjuhëve të popullarizuara dhe aktivisht të përdorura me instrumente dhe biblioteka të zhvilluar. Të programosh në një gjuhë të tipit stack është mjaft e pakëndshme, pasi duhet të mbash mend vazhdimisht se çfarë është ku në stack, dhe kompajleri nuk ndihmon në këtë.

Prandaj, justifikimi i vetëm, sipas mendimit tonë, për ekzistencën e Fift është roli i tij si gjuhë host për Fift Assembler. Por a nuk do të ishte më mirë të integrohej asambleja e TVM në ndonjë gjuhë ekzistuese, sesa të krijohej një e re për këtë, në esencë, qëllim të vetëm?

TVM Haskell eDSL

Tani ka ardhur koha për t'i treguar rreth kontraktit tonë të dytë të mençur. Ndezëm të zhvillonim një portofol me shumë nënshkresa, por të shkruajmë një kontrakt të ri të mençur në FunC do të ishte tepër e mërzitshme. Donim të shtonim ndonjë pikë të veçantë, dhe ajo ishte gjuha jonë e vetme e asamblesë për TVM.

Si dhe Fift Assembler, gjuha jonë e re është e integruar, vetëm se si host zgjodhëm Haskell, duke na lejuar të shfrytëzojmë sistemin e tij të avancuar të llojeve. Gjatë punës me kontraktet e mençura, ku çmimi i një gabimi të vogël mund të jetë shumë i lartë, tipizimi statik, sipas mendimit tonë, është një avantazh i madh.

Për të demonstruar se si duket asambleja e TVM e integruar në Haskell, ne implementuam një portofol standard në të. Ja disa gjëra që meritojnë vëmendje:

  • Ky questo kontrakt Ă«shtĂ« i pĂ«rbĂ«rĂ« nga njĂ« funksion, por ju mund tĂ« pĂ«rdorni aq shumĂ« sa tĂ« dĂ«shironi. Kur pĂ«rcaktoni njĂ« funksion tĂ« ri nĂ« gjuhĂ«n pritĂ«se (dmth, nĂ« Haskell), eDSL ynĂ« ju lejon tĂ« zgjidhni nĂ«se dĂ«shironi qĂ« ai tĂ« kthehet nĂ« njĂ« nĂ«nprogram tĂ« veçantĂ« nĂ« TVM apo thjesht tĂ« pĂ«rfshihet nĂ« vendin e thirrjes.
  • Si nĂ« Haskell, funksionet kanĂ« tipe qĂ« verifikohen gjatĂ« kompilimit. NĂ« eDSL tonĂ« tipi i hyrjes sĂ« funksionit Ă«shtĂ« tipi i grumbullit qĂ« funksioni pret, dhe tipi i rezultatit Ă«shtĂ« tipi i grumbullit qĂ« do tĂ« del pas thirrjes.
  • Ka anotacione nĂ« kod stacktype, qĂ« pĂ«rshkruajnĂ« tipin e pritur tĂ« grumbullit nĂ« pikĂ«n e thirrjes. NĂ« kontraktin origjinal tĂ« portofolit, ato ishin thjesht komente, por nĂ« eDSL tonĂ« ato janĂ« nĂ« fakt pjesĂ« e kodit dhe kontrollohen gjatĂ« kompilimit. Ato mund tĂ« shĂ«rbejnĂ« si dokumentacion ose deklarata qĂ« ndihmojnĂ« zhvilluesin tĂ« gjejĂ« njĂ« problem nĂ« rast se tipi i grumbullit ndryshon gjatĂ« ndryshimit tĂ« kodit. Natyrisht, kĂ«to anotacione nuk ndikojnĂ« nĂ« performancĂ«n gjatĂ« ekzekutimit, pasi pĂ«r to nuk gjenerohet ndonjĂ« kod TVM.
  • Ky Ă«shtĂ« ende njĂ« prototip, shkruar brenda dy javĂ«ve, prandaj projekti ka ende shumĂ« punĂ« pĂ«r t'u bĂ«rĂ«. PĂ«r shembull, tĂ« gjitha instancat e klasave qĂ« shihni nĂ« kodin e mĂ«poshtĂ«m duhet tĂ« krijohen automatikisht.

Ja si duket implementimi i portofolit multisig në eDSL tonë:

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 @Word32

Kodin e plotë burimor të eDSL tonë dhe kontraktin e portofolit multisig mund ta gjeni në këtë repozitor. Ndërsa më detajisht foli rreth gjuhëve të integruara kolegu ynë Georgiy Agapov.

Përfundimet mbi garën dhe TON

Në total, puna jonë zuri 380 orë (përfshirë njohjen me dokumentacionin, takimet dhe zhvillimin në vetvete). Në projektin konkurues morën pjesë pesë zhvillues: CTO, lideri i ekipit, specialistë të platformave blockchain dhe zhvillues të software-it në Haskell.

Burimet pĂ«r tĂ« marrĂ« pjesĂ« nĂ« konkurs i gjetĂ«m pa vĂ«shtirĂ«si, sepse fryma e hackathon-it, puna e ngushtĂ« nĂ« ekip, nevoja pĂ«r njĂ« zhytje tĂ« shpejtĂ« nĂ« aspektet e teknologjive tĂ« reja — Ă«shtĂ« gjithmonĂ« erĂ« dhe interesante. Disa natĂ« pa gjumĂ« pĂ«r tĂ« arritur rezultate maksimale nĂ« kushte tĂ« kufizuara burimesh kompensohen me pĂ«rvojĂ« tĂ« çmuar dhe kujtime tĂ« shkĂ«lqyera. PĂ«r mĂ« tepĂ«r, puna nĂ« kĂ«tĂ« lloj detyrash Ă«shtĂ« gjithmonĂ« njĂ« test i mirĂ« pĂ«r proceset e kompanisĂ«, pasi tĂ« arrihen rezultate me vlerĂ« pa njĂ« bashkĂ«punim tĂ« shkĂ«lqyer tĂ« brendshĂ«m Ă«shtĂ« jashtĂ«zakonisht e vĂ«shtirĂ«.

Përveç lirizmit: ne ishim të impresionuar nga volumi i punës të bërë nga ekipi TON. Ata arritën të ndërtuan një sistem të ndërlikuar, të bukur, dhe më e rëndësishmja, funksional. TON u tregua si një platformë me potencial të madh. Megjithatë, për të siguruar që kjo ekosistem të zhvillohet, ka ende shumë punë për t'u bërë, si në aspektin e përdorimit të saj në projektet blockchain, ashtu edhe në aspektin e përmirësimit të mjeteve të zhvillimit. Ne jemi të krenarë që tani jemi pjesë e këtij procesi.

NĂ«se pas leximit tĂ« kĂ«tij artikulli ju mbetĂ«n pyetje ose keni ide se si tĂ« aplikoni TON pĂ«r zgjidhjen e detyrave tuaja, na shkruani — ne me kĂ«naqĂ«si do tĂ« ndajmĂ« pĂ«rvojĂ«n tonĂ«.

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster