Ginagawang FunCtional ang FunC kasama ang Haskell: Paano nanalo si Serokell sa Telegram Blockchain Competition

Marahil ay narinig mo na ang Telegram na iyon ay malapit nang ilunsad ang Ton blockchain platform. Ngunit maaaring napalampas mo ang balita na hindi pa nagtagal Telegram nagpahayag ng kompetisyon para sa pagpapatupad ng isa o higit pang matalinong kontrata para sa platform na ito.

Ang pangkat ng Serokell, na may malawak na karanasan sa pagbuo ng malalaking proyekto ng blockchain, ay hindi maaaring tumabi. Nag-delegate kami ng limang empleyado sa kumpetisyon, at pagkaraan ng dalawang linggo ay nakuha nila ang unang pwesto dito sa ilalim ng (sa) modest random na palayaw na Sexy Chameleon. Sa artikulong ito ay pag-uusapan ko kung paano nila ito ginawa. Inaasahan namin na sa susunod na sampung minuto ay makakabasa ka ng isang kawili-wiling kuwento, at higit sa lahat ay makakahanap ka ng isang bagay na kapaki-pakinabang dito na maaari mong ilapat sa iyong trabaho.

Ngunit magsimula tayo sa isang maliit na konteksto.

Kumpetisyon at mga kondisyon nito

Kaya, ang mga pangunahing gawain ng mga kalahok ay ang pagpapatupad ng isa o higit pa sa mga iminungkahing matalinong kontrata, pati na rin ang paggawa ng mga panukala upang mapabuti ang TON ecosystem. Ang kumpetisyon ay tumakbo mula Setyembre 24 hanggang Oktubre 15, at ang mga resulta ay inihayag lamang noong Nobyembre 15. Medyo matagal na panahon, isinasaalang-alang na sa panahong ito pinamamahalaang ng Telegram na hawakan at ipahayag ang mga resulta ng mga paligsahan sa disenyo at pag-unlad ng mga aplikasyon sa C++ para sa pagsubok at pagtatasa ng kalidad ng mga tawag sa VoIP sa Telegram.

Pumili kami ng dalawang matalinong kontrata mula sa listahang iminungkahi ng mga organizer. Para sa isa sa kanila, gumamit kami ng mga tool na ipinamahagi sa TON, at ang pangalawa ay ipinatupad sa isang bagong wika na binuo ng aming mga inhinyero partikular para sa TON at binuo sa Haskell.

Ang pagpili ng isang functional programming language ay hindi sinasadya. Sa aming corporate blog Madalas nating pinag-uusapan kung bakit sa tingin natin ang pagiging kumplikado ng mga functional na wika ay isang malaking pagmamalabis at kung bakit mas gusto natin ang mga ito kaysa sa mga object-oriented. Sa pamamagitan ng paraan, naglalaman din ito orihinal ng artikulong ito.

Bakit nga ba tayo nagpasya na lumahok?

Sa madaling salita, dahil ang aming espesyalisasyon ay hindi karaniwan at kumplikadong mga proyekto na nangangailangan ng mga espesyal na kasanayan at kadalasan ay may halagang pang-agham sa komunidad ng IT. Lubos naming sinusuportahan ang open-source na pag-unlad at nakikibahagi sa pagpapasikat nito, at nakikipagtulungan din sa mga nangungunang unibersidad sa Russia sa larangan ng computer science at matematika.

Ang mga kagiliw-giliw na gawain ng kumpetisyon at paglahok sa aming minamahal na proyekto sa Telegram ay sa kanilang sarili ay isang mahusay na pagganyak, ngunit ang pondo ng premyo ay naging isang karagdagang insentibo. πŸ™‚

Pananaliksik ng TON blockchain

Mahigpit naming sinusubaybayan ang mga bagong pag-unlad sa blockchain, artificial intelligence at machine learning at sinisikap naming huwag makaligtaan ang isang makabuluhang release sa bawat lugar kung saan kami nagtatrabaho. Samakatuwid, sa oras na nagsimula ang kumpetisyon, ang aming koponan ay pamilyar na sa mga ideya mula sa TON puting papel. Gayunpaman, bago simulan ang trabaho sa TON, hindi namin sinuri ang teknikal na dokumentasyon at ang aktwal na source code ng platform, kaya ang unang hakbang ay medyo halata - isang masusing pag-aaral ng opisyal na dokumentasyon sa Online at mga imbakan ng proyekto.

Sa oras na nagsimula ang kompetisyon, nai-publish na ang code, kaya para makatipid ng oras, nagpasya kaming maghanap ng gabay o buod na isinulat ni mga gumagamit. Sa kasamaang palad, hindi ito nagbigay ng anumang mga resulta - bukod sa mga tagubilin para sa pag-assemble ng platform sa Ubuntu, wala kaming nakitang iba pang mga materyales.

Ang dokumentasyon mismo ay mahusay na sinaliksik, ngunit mahirap basahin sa ilang mga lugar. Kadalasan kailangan naming bumalik sa ilang partikular na punto at lumipat mula sa mataas na antas na paglalarawan ng mga abstract na ideya patungo sa mababang antas ng mga detalye ng pagpapatupad.

Magiging mas madali kung ang detalye ay hindi kasama ang isang detalyadong paglalarawan ng pagpapatupad sa lahat. Ang impormasyon tungkol sa kung paano kinakatawan ng isang virtual machine ang stack nito ay mas malamang na makagambala sa mga developer na lumilikha ng mga matalinong kontrata para sa platform ng TON kaysa tulungan sila.

Nix: pagsasama-sama ng proyekto

Sa Serokel kami ay malaking tagahanga ala. Kinokolekta namin ang aming mga proyekto sa kanila at ginagamit ang mga ito NixOps, at naka-install sa lahat ng aming mga server Nix OS. Dahil dito, ang lahat ng aming mga build ay maaaring kopyahin at gumagana sa anumang operating system kung saan maaaring mai-install ang Nix.

Kaya nagsimula kami sa paglikha Nix overlay na may expression para sa TON assembly. Sa tulong nito, ang pag-compile ng TON ay kasing simple hangga't maaari:

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

Tandaan na hindi mo kailangang mag-install ng anumang mga dependency. Kakaibang gagawin ni Nix ang lahat para sa iyo, gumagamit ka man ng NixOS, Ubuntu, o macOS.

Programming para sa TON

Ang smart contract code sa TON Network ay tumatakbo sa TON Virtual Machine (TVM). Ang TVM ay mas kumplikado kaysa sa karamihan ng iba pang mga virtual machine, at may napakakawili-wiling pag-andar, halimbawa, maaari itong gumana mga pagpapatuloy ΠΈ mga link sa data.

Bukod dito, ang mga lalaki mula sa TON ay lumikha ng tatlong bagong programming language:

Limang ay isang unibersal na stack programming language na kahawig Forth. Ang super ability niya ay ang kakayahang makipag-interact sa TVM.

KasayahanC ay isang smart contract programming language na katulad ng C at pinagsama-sama sa ibang wika - Fift Assembler.

Ikalimang Assembler β€” Fift library para sa pagbuo ng binary executable code para sa TVM. Ang Fifth Assembler ay walang compiler. Ito Naka-embed na Domain Specific Language (eDSL).

Gumagana ang ating kumpetisyon

Sa wakas, oras na upang tingnan ang mga resulta ng aming mga pagsisikap.

Asynchronous na channel ng pagbabayad

Ang channel ng pagbabayad ay isang matalinong kontrata na nagpapahintulot sa dalawang user na magpadala ng mga pagbabayad sa labas ng blockchain. Bilang isang resulta, hindi ka lamang nag-iipon ng pera (walang komisyon), kundi pati na rin ang oras (hindi mo kailangang maghintay para sa susunod na bloke na maproseso). Ang mga pagbabayad ay maaaring kasing liit ng ninanais at kasingdalas ng kinakailangan. Sa kasong ito, ang mga partido ay hindi kailangang magtiwala sa isa't isa, dahil ang pagiging patas ng pinal na kasunduan ay ginagarantiyahan ng matalinong kontrata.

Nakakita kami ng medyo simpleng solusyon sa problema. Dalawang partido ang maaaring magpalitan ng mga nilagdaang mensahe, bawat isa ay naglalaman ng dalawang numeroβ€”ang buong halagang binayaran ng bawat partido. Gumagana ang dalawang numerong ito vector clock sa mga tradisyunal na distributed system at itakda ang order na "nangyari noon" sa mga transaksyon. Gamit ang data na ito, malulutas ng kontrata ang anumang posibleng salungatan.

Sa katunayan, sapat na ang isang numero para ipatupad ang ideyang ito, ngunit iniwan namin pareho dahil sa paraang ito ay makakagawa kami ng mas maginhawang user interface. Bilang karagdagan, nagpasya kaming isama ang halaga ng pagbabayad sa bawat mensahe. Kung wala ito, kung ang mensahe ay nawala para sa ilang kadahilanan, kung gayon, kahit na ang lahat ng mga halaga at ang panghuling pagkalkula ay tama, maaaring hindi mapansin ng gumagamit ang pagkawala.

Upang subukan ang aming ideya, naghanap kami ng mga halimbawa ng paggamit ng ganoong simple at maigsi na protocol ng channel ng pagbabayad. Nakakagulat, dalawa lang ang nakita namin:

  1. ОписаниС isang katulad na diskarte, para lamang sa kaso ng isang unidirectional channel.
  2. Pagtuturo, na naglalarawan ng kaparehong ideya gaya ng sa amin, ngunit hindi ipinapaliwanag ang maraming mahahalagang detalye, gaya ng pangkalahatang kawastuhan at mga pamamaraan sa paglutas ng salungatan.

Naging malinaw na makatuwirang ilarawan ang aming protocol nang detalyado, na binibigyang pansin ang kawastuhan nito. Pagkatapos ng ilang pag-ulit, handa na ang pagtutukoy, at ngayon ay magagawa mo rin. tumingin sa kanya.

Ipinatupad namin ang kontrata sa FunC, at isinulat namin ang command line utility para sa buong pakikipag-ugnayan sa aming kontrata sa Fift, gaya ng inirerekomenda ng mga organizer. Maaari kaming pumili ng anumang iba pang wika para sa aming CLI, ngunit interesado kaming subukan ang Fit upang makita kung paano ito gumanap sa pagsasanay.

Sa totoo lang, pagkatapos magtrabaho kasama ang Fift, wala kaming nakitang anumang nakakahimok na dahilan para mas gusto ang wikang ito kaysa sa mga sikat at aktibong ginagamit na mga wika na may mga binuo na tool at library. Ang programming sa isang stack-based na wika ay medyo hindi kasiya-siya, dahil kailangan mong patuloy na panatilihin sa iyong ulo kung ano ang nasa stack, at ang compiler ay hindi makakatulong dito.

Samakatuwid, sa aming opinyon, ang tanging katwiran para sa pagkakaroon ng Fift ay ang papel nito bilang host language para sa Fift Assembler. Ngunit hindi ba mas mainam na i-embed ang TVM assembler sa ilang umiiral na wika, sa halip na mag-imbento ng bago para sa mahalagang layuning ito?

TVM Haskell eDSL

Ngayon ay oras na para pag-usapan ang aming pangalawang matalinong kontrata. Nagpasya kaming bumuo ng isang multi-signature na wallet, ngunit ang pagsusulat ng isa pang matalinong kontrata sa FunC ay magiging masyadong boring. Nais naming magdagdag ng ilang lasa, at iyon ang aming sariling wika ng pagpupulong para sa TVM.

Tulad ng Fift Assembler, ang aming bagong wika ay naka-embed, ngunit pinili namin ang Haskell bilang host sa halip na Fift, na nagpapahintulot sa amin na lubos na mapakinabangan ang advanced na uri ng system nito. Kapag nagtatrabaho sa mga matalinong kontrata, kung saan ang halaga ng kahit isang maliit na error ay maaaring maging napakataas, ang static na pag-type, sa aming opinyon, ay isang malaking kalamangan.

Upang ipakita kung ano ang hitsura ng TVM assembler na naka-embed sa Haskell, nagpatupad kami ng karaniwang wallet dito. Narito ang ilang bagay na dapat bigyang pansin:

  • Ang kontratang ito ay binubuo ng isang function, ngunit maaari mong gamitin ang marami hangga't gusto mo. Kapag nagtakda ka ng bagong function sa host language (i.e. Haskell), binibigyang-daan ka ng aming eDSL na piliin kung gusto mo itong maging isang hiwalay na routine sa TVM o naka-inline lang sa punto ng tawag.
  • Tulad ng Haskell, ang mga function ay may mga uri na sinusuri sa oras ng pag-compile. Sa aming eDSL, ang uri ng input ng isang function ay ang uri ng stack na inaasahan ng function, at ang uri ng resulta ay ang uri ng stack na gagawin pagkatapos ng tawag.
  • Ang code ay may mga anotasyon stacktype, na naglalarawan sa inaasahang uri ng stack sa call point. Sa orihinal na kontrata ng wallet ang mga ito ay mga komento lamang, ngunit sa aming eDSL ay bahagi talaga sila ng code at sinusuri sa oras ng pag-compile. Maaari silang magsilbi bilang dokumentasyon o mga pahayag na makakatulong sa developer na mahanap ang problema kung magbabago ang code at magbabago ang uri ng stack. Siyempre, ang mga naturang anotasyon ay hindi nakakaapekto sa pagganap ng runtime, dahil walang TVM code na nabuo para sa kanila.
  • Isa pa rin itong prototype na isinulat sa loob ng dalawang linggo, kaya marami pa ring kailangang gawin sa proyekto. Halimbawa, ang lahat ng mga pagkakataon ng mga klase na nakikita mo sa code sa ibaba ay dapat na awtomatikong mabuo.

Ganito ang hitsura ng pagpapatupad ng multisig wallet sa aming 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 @Word32

Ang buong source code ng aming eDSL at multi-signature wallet na kontrata ay makikita sa imbakan na ito. At iba pa sinabi nang detalyado tungkol sa mga built-in na wika, ang aming kasamahan na si Georgy Agapov.

Mga konklusyon tungkol sa kumpetisyon at TON

Sa kabuuan, ang aming trabaho ay tumagal ng 380 oras (kabilang ang pamilyar sa dokumentasyon, mga pagpupulong at aktwal na pag-unlad). Limang developer ang nakibahagi sa proyekto ng kompetisyon: CTO, team lead, blockchain platform specialists at Haskell software developers.

Nakahanap kami ng mga mapagkukunan upang lumahok sa paligsahan nang walang kahirapan, dahil ang diwa ng isang hackathon, malapit na pagtutulungan ng magkakasama, at ang pangangailangan na mabilis na isawsaw ang ating sarili sa mga aspeto ng mga bagong teknolohiya ay palaging kapana-panabik. Maraming mga gabing walang tulog upang makamit ang pinakamataas na resulta sa mga kondisyon ng limitadong mapagkukunan ay binabayaran ng napakahalagang karanasan at magagandang alaala. Bilang karagdagan, ang pagtatrabaho sa gayong mga gawain ay palaging isang mahusay na pagsubok sa mga proseso ng kumpanya, dahil napakahirap na makamit ang tunay na disenteng mga resulta nang walang mahusay na gumaganang panloob na pakikipag-ugnayan.

Lyrics aside: humanga kami sa dami ng trabahong inilagay ng TON team. Nagawa nilang bumuo ng isang kumplikado, maganda, at higit sa lahat, gumaganang sistema. Napatunayan ng TON ang sarili bilang isang platform na may malaking potensyal. Gayunpaman, para umunlad ang ecosystem na ito, marami pang kailangang gawin, kapwa sa paggamit nito sa mga proyekto ng blockchain at sa mga tuntunin ng pagpapabuti ng mga tool sa pag-unlad. Ipinagmamalaki namin na ngayon ay bahagi kami ng prosesong ito.

Kung pagkatapos basahin ang artikulong ito ay mayroon ka pa ring mga katanungan o may mga ideya kung paano gamitin ang TON upang malutas ang iyong mga problema, sumulat sa amin β€” ikalulugod naming ibahagi ang aming karanasan.

Pinagmulan: www.habr.com

Magdagdag ng komento