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

Ju keni dëgjuar me siguri se Telegram po planifikon të nisë platformën blockchain Ton. Por ndoshta keni humbur lajmin se së fundmi Telegram deklaroi një konkurs për implementimin e një ose më shumë kontratave të mençura për këtë platformë.

Ekipi 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 pas dy javësh ata e fituan atë vend të parë me një emër (në)esk modest, Sexy Chameleon. Në këtë artikull, do të flas për mënyrën si ia dolën. Kemi shpresë se për dhjetë minutat e ardhshme, ju do të lexoni të paktën një histori interesante, dhe më shumë mund të gjeni diçka të dobishme që mund ta aplikoni në punën tuaj.

Por le të fillojmë me një përqasje të vogël në kontekst.

Konkursi dhe kushtet e tij

Pra, detyrat kryesore të pjesëmarrësve ishin implementimi i një ose më shumë të kontratave të propozuara të mençura, si dhe paraqitja e propozimeve për përmirësimin e ekosistemit TON. Konkursi u mbajt nga 24 Shtatori deri më 15 Tetor dhe rezultatet u shpallën vetëm më 15 Nëntor. Mjaft e gjatë, duke marrë parasysh se gjatë kësaj periudhe Telegram kishte arritur të zhvillonte dhe shpallte rezultatet e konkurseve për dizajn dhe zhvillim aplikacionesh 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 të mençura. Për një prej tyre përdorëm mjetet që shpërndahen me TON, ndërsa të dytën e realizuam në një gjuhë të re, të zhvilluar nga inxhinierët tanë veçmas për TON dhe të integruar në Haskell.

Zgjedhja e një gjuhe funksionale programimi nuk është rastësore. Në blogun tonë korporativ ne shpesh flasim për arsyet pse e konsiderojmë kompleksitetin e gjuhëve funksionale si një teprim të madh, dhe pse në përgjithësi i preferojmë ato mbi ato të orientuara nga objekti. Sidoqoftë, në të ka edhe origjinalin e këtij artikulli.

Pse vendosëm të marrim pjesë

Në përmbledhje, sepsespecializimi ynë është në projekte jo standarde dhe komplekse, që kërkojnë aftësi të veçanta dhe shpesh përbëjnë vlerë shkencore për komunitetin IT. Ne e mbështesim me pasion zhvillimin open-source dhe merremi me popullarizimin e tij, dhe gjithashtu bashkëpunojmë me universitetet kryesore të Rusisë në fushën e shkencave kompjuterike dhe matematikës.

Sfidhi konkurrencës dhe angazhimi në projektin që e duam shumë, Telegrami, ishte një motivim i shkëlqyer dhe fondi i çmimeve ishte një stimulus shtesë. 🙂

Kërkimi mbi blockchain-in TON

Ne monitorojmë me vëmendje zhvillimet e reja në blockchain, inteligjencën artificiale dhe mësimin e makinerive dhe përpiqemi të mos humbasim asnjë publikim të rëndësishëm në secilën nga fushat në të cilat punojmë. Prandaj, në momentin e fillimit të konkurrencës, ekipi ynë tashmë ishte njohur me idetë nga ton white paper. Megjithatë, deri në fillimin e punës me TON, ne nuk kishim analizuar dokumentacionin teknik dhe kodin burimor të platformës, prandaj hapi i parë ishte mjaft i qartë - një studim i thellë i dokumentacionit zyrtar në website dhe në repositori i projektit.

Në fillim të konkurrencës, kodi tashmë ishte publikuar, prandaj, për të kursyer kohë, vendosëm të kërkojmë një udhëzues ose përmbledhje të shkruar nga përdoruesit. Fatkeqësisht, kjo nuk ishte shumë rezultative - përveç udhëzimit për ndërtimin e platformës në Ubuntu, nuk gjetëm materiale të tjera.

Dokumentacioni vetë rezultoi të ishte i punuar me kujdes, por leximi i tij në disa momente ishte i vështirë. Mjaft shpesh na duhej të ktheheshim në pika të ndryshme dhe të kalonim nga përshkrime të nivelit të lartë të ideve abstrakte në detaje të nivelit të ulët të realizimit.

Do të ishte më e lehtë nëse specifikimi nuk do të kishte ndonjë përshkrim të detajuar të realizimit. Informacioni se si mašina virtuale paraqet stekën e saj, më tepër e shpërqendron zhvilluesit që krijojnë kontrata inteligente për platformën TON, sesa i ndihmon ata.

Nix: ndërtimi i projektit

Në Serokell ne jemi adhurues të mëdhenj të Nix. Ne i ndjejmë projektet tona me të dhe i zbatojmë ato me NixOps, dhe në të gjitha serverët tanë është instaluar NixOS. Kjo na lejon që të gjitha ndërtimet tona të jenë të riprodhueshme dhe të funksionojnë në çdo sistem operativ, në të cilin mund të instalohet Nix.

Prandaj, filluam me krijimin e Nix overlay me shprehjen për ndërtimin e TON. Me ndihmën e tij, të kompilosh TON është shumë e lehtë:

$ 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 do të bëjë çdo gjë magjikisht për ju pa marrë parasysh nëse përdorni NixOS, Ubuntu ose macOS.

Programimi për TON

Kodi i kontratave të mençura në Rrjetin TON ekzekutohet në TON Virtual Machine (TVM). TVM është më e ndërlikuar se shumica e makinave të tjera virtuale dhe ka funksionalitete shumë interesante, për shembull, ajo mund të punojë me vazhdate (continuations) dhe lidhet me të dhëna.

Për më tepër, djemtë nga TON krijuan tre gjuhë të reja programimi:

Fift — një gjuhë universale programuese me gjendje (stack-based), që i ngjan Forth. Superfuqia e saj është mundësia për t'u ndërvepruar me TVM.

FunC — një gjuhë programimi për kontrata të mençura që ngjason me C dhe kompilohet 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 një kompilator. Ky është një gjuhë (eDSL) e integruar me objekt..

Punimet tona konkurente

Më në fund, erdhi koha të shikojmë rezultatet e përpjekjeve tona.

Kanalet e pagesave asinkrone

Kanalet e pagesave (payment channels) janë kontrata të mençura që lejojnë dy përdorues të dërgojnë pagesa jashtë blockchain-it. Si rezultat, jo vetëm që kurseni para (nuk ka komision), por edhe kohë (nuk keni nevojë të prisni në procesin e bllokut tjetër). Pagesat mund të jenë sa të vogla dhe të ndodhin aq shpesh sa është e nevojshme. Në të njëjtën kohë, palët nuk është e nevojshme të besojnë njëra-tjetrën, pasi drejtësia e llogaritjes përfundimtare garantohet nga kontrata e mençur.

Kemi gjetur një zgjidhje të mjaftueshme për problemin. Dy palët mund të shkëmbejnë mesazhe të nënshkruara, secili prej të cilëve përmban dy numra — shumën totale të paguar nga secili nga pjesëmarrësit. Këta dy numra funksionojnë si ora vektoriale në sistemet e shpërndara tradicionale dhe përcaktojnë rendin "ka ndodhur para" në transaksione. Duke përdorur këto të dhëna, kontrata do të jetë në gjendje të zgjidhë çdo konflikt të mundshëm.

Në të vërtetë, për realizimin e kësaj ideje mjafton edhe një numër, por ne e lamë të dyja, pasi në këtë mënyrë mundëm të krijojmë një ndërfaqe më të rehatshme për përdoruesin. Përveç kësaj, vendosëm që të përfshijmë në çdo mesazh madhësinë e pagesës. Pa të, nëse mesazhi humbet për ndonjë arsye, megjithëse të gjitha shumat dhe llogaritja përfundimtare do të ishin të sakta, përdoruesi mund të mos vërehet humbjen.

Për të verifikuar idenë tonë, ne kërkuam shembuj të përdorimit të një protokolli të tillë të thjeshtë dhe të qartë të kanalit të pagesës. Për habinë tonë, gjetëm vetëm dy:

  1. Përshkrimi një qasje të ngjashme, vetëm për rastin e një kanali njëdrejtues.
  2. Tutorial, në të cilin përshkruhet po e njëjta ide si e jona, vetëm pa shpjegimin e detajeve të shumta të rëndësishme, si korrektësia e përgjithshme dhe procedura e zgjidhjes së konflikteve.

U bë e qartë se ka kuptim të përshkruajmë në detaje protokollin tonë, duke i kushtuar vëmendje të veçantë korrektësisë së tij. Pas disa iteracioneve, specifikimi ishte gati, dhe tani ju gjithashtu mund të shikoni atë.

Ne realizuam kontratën në FunC, ndërsa utilitarin e linjës së komandës për të ndërvepruar me kontratën tonë e kemi shkruar plotësisht në Fift, ashtu siç rekomanduan organizatorët. Ne mund të kishim zgjedhur çfarëdo gjuhe tjetër për CLI-në tonë, por na kishte interes ta provonim pikërisht Fift, për të parë si do t'i shfaqej vetja në praktikë.

Sinqerisht, pasi punuam me Fift, ne nuk pamë arsye të forta për ta preferuar këtë gjuhë ndaj gjuhëve më popullore dhe aktivisht të përdorura me mjete dhe biblioteka të zhvilluar. Programimi në një gjuhë me të dhëna është mjaft i dhimbshëm, pasi duhet të mbash gjithmonë mend se çfarë ndodhet ku në të dhënat, dhe kompajleri nuk ndihmon në këtë.

Prandaj, e vetmja justifikim, sipas mendimit tonë, për ekzistencën e Fift është roli i tij si gjuhë host për Fift Assembler. Por nuk do të ishte më mirë të integrohej asmbler TVM në ndonjë gjuhë ekzistuese, përpara se të shpiknim një të re për këtë qëllim, në thelb, të vetëm?

TVM Haskell eDSL

Tani ka ardhur koha të flasim për kontratën tonë të dytë të zgjuar. Ne vendosëm të zhvillojmë një portofol me shumë nënshkrime, por të shkruajmë një tjetër kontratë të zgjuar në FunC do të ishte tepër e mërzitshme. Na pëlqente të shtonim ndonjë element të veçantë, dhe ai ishte gjuha jonë e vetme e asmblerit për TVM.

Ashtu si Fift Assembler, gjuha jonë e re është e integrueshme, vetëm se si host në vend të Fift, ne zgjodhëm Haskell, që na lejoi të shfrytëzojmë plotësisht sistemin e tij të avancuar të tipeve. Kur punoni me kontratat e zgjuara, 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 assembleri TVM, i integruar në Haskell, ne realizuam një portofol standard. Ja disa gjëra për të cilat duhet të keni parasysh:

  • Ky kontratë përbëhet nga një funksion, por ju mund të përdorni sa më shumë që dëshironi. Kur përcaktoni një funksion të ri në gjuhën e hostit (dmth. në Haskell), eDSL jonë ju lejon të zgjidhni nëse dëshironi që ai të shndërrohet në një nënprogram të veçantë në TVM ose thjesht të integrohet në vendin e thirrjes.
  • Si në Haskell, funksionet kanë tipe që kontrollohen gjatë kompaktimit. Në eDSL tonë, tipi i hyrjes së funksionit është tipi i shtakës që funksioni pret, ndërsa tipi i rezultatit është tipi i shtakës që do të rezultojë pas thirrjes.
  • Në kod ka annotime stacktype, të cilat përshkruajnë tipi e pritur të shtakës në pikën e thirrjes. Në kontratën origjinale të portofolit, këto ishin thjesht komente, por në eDSL tonë ato në fakt janë pjesë e kodit dhe kontrollohen gjatë kompaktimit. Ato mund të shërbejnë si dokumentacion ose deklarata që ndihmojnë programuesin të gjejë problemin nëse, gjatë ndryshimeve në kod, tipi i shtakës ndryshon. Natyrisht, këto annotime nuk ndikojnë në performancën gjatë ekzekutimit, pasi asnjë kod TVM nuk gjenerohet për to.
  • Ky është ende një prototip, i shkruar brenda dy javëve, kështu që ka ende shumë punë për të bërë në projekt. Për shembull, të gjitha instancat e klasave që shihni në kodin më poshtë duhet të gjenerohen automatikisht.

Këtu është si duket realizimi 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

Kodu i plotë burimor i eDSL tonë dhe kontrata e portofolit me shumë nënshkrim mund të gjeni në këtë depo. Një më të detajuar tregoi për gjuhët e integruara kolegu ynë Georgiy Agapov.

Konkluzionet për garën dhe TON

Në total, puna jonë zuri 380 orë (duke përfshirë njohjen me dokumentacionin, takimet dhe zhvillimin e drejtpërdrejt). Në projektin e konkursit morën pjesë pesë zhvillues: CTO, lideri i ekipit, specialistët e platformave blockchain dhe zhvilluesit e softuerit në Haskell.

Burimet për pjesëmarrjen në konkurs i gjetëm lehtësisht, pasi fryma e hackathon-it, puna e ngushtë në ekip, nevoja për të hyrë shpejt në aspektet e teknologjive të reja - gjithmonë është emocionuese. Disa net pa gjumë për të arritur rezultate maksimale në kushte burimesh të kufizuara kompenzohen nga përvoja të paçmuara dhe kujtime të shkëlqyera. Për më tepër, puna mbi këto detyra është gjithmonë një verifikim i mirë i proceseve të kompanisë, pasi të arrish rezultate të vërteta të denja pa një ndërveprim të brendshëm të përshtatshëm është jashtëzakonisht e vështirë.

Përveç lirizmit: ne ishim të impresionuar nga volumi i punës së kryer nga ekipi TON. Ata arritën të ndërtuan një sistem të ndërlikuar, të bukur dhe, mbi të gjitha, funksional. TON e tregoi veten si një platformë me potencial të madh. Megjithatë, për të zhvilluar këtë ekosistem, duhet të bëhet ende shumë, si sa i përket përdorimit të tij në projektet blockchain, ashtu edhe në përmirësimin e mjeteve të zhvillimit. Ne jemi krenarë që tani jemi pjesë e këtij procesi.

Nëse pas leximit të këtij artikulli ju kanë mbetur ndonjë pyetje ose keni ide se si të aplikoni TON për zgjidhjen e detyrave tuaja, na shkruani — ne do të jemi të lumtur të ndihmojmë me përvojën tonë.

Burimi: habr.com

Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS 🔥 Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS - ProHoster