Haskell bilan FunC-ni FunCtional-ga aylantirish: Serokell qanday qilib Telegram blokcheyn tanlovida g'olib chiqdi

Siz bu Telegramni eshitgandirsiz Ton blokcheyn platformasini ishga tushirish arafasida. Ammo yaqinda Telegramdagi yangiliklarni o'tkazib yuborgan bo'lishingiz mumkin tanlov e’lon qildi ushbu platforma uchun bir yoki bir nechta aqlli shartnomalarni amalga oshirish uchun.

Katta blokcheyn loyihalarini ishlab chiqishda katta tajribaga ega Serokell jamoasi chetda turolmadi. Biz besh nafar xodimni tanlovga topshirdik va ikki hafta o'tgach, ular Sexy Chameleon (in) oddiy tasodifiy taxallus ostida birinchi o'rinni egalladilar. Ushbu maqolada men ular buni qanday qilgani haqida gapiraman. Umid qilamizki, keyingi o'n daqiqa ichida siz hech bo'lmaganda qiziqarli hikoyani o'qiysiz va ko'pi bilan unda siz o'zingizning ishingizda qo'llashingiz mumkin bo'lgan foydali narsani topasiz.

Ammo keling, bir oz kontekstdan boshlaylik.

Raqobat va uning shartlari

Shunday qilib, ishtirokchilarning asosiy vazifalari taklif qilingan bir yoki bir nechta aqlli shartnomalarni amalga oshirish, shuningdek, TON ekotizimini yaxshilash bo'yicha takliflar kiritish edi. Tanlov 24 sentyabrdan 15 oktyabrgacha davom etdi va natijalar faqat 15 noyabr kuni e'lon qilindi. Uzoq vaqt davomida Telegram Telegram’da VoIP qo‘ng‘iroqlari sifatini sinovdan o‘tkazish va baholash uchun C++ tilida ilovalarni loyihalash va ishlab chiqish bo‘yicha tanlovlar o‘tkazish va natijalarini e’lon qilishga muvaffaq bo‘lganini hisobga olsak.

Tashkilotchilar tomonidan taklif qilingan ro'yxatdan ikkita aqlli shartnomani tanladik. Ulardan biri uchun biz TON bilan tarqatilgan vositalardan foydalandik, ikkinchisi esa bizning muhandislarimiz tomonidan maxsus TON uchun ishlab chiqilgan va Haskell-ga o'rnatilgan yangi tilda amalga oshirildi.

Funktsional dasturlash tilini tanlash tasodifiy emas. Bizning korporativ blog Biz ko'pincha nima uchun funktsional tillarning murakkabligi katta mubolag'a deb o'ylashimiz va nima uchun ularni ob'ektga yo'naltirilgan tillardan afzal ko'rishimiz haqida gapiramiz. Aytgancha, u ham o'z ichiga oladi ushbu maqolaning asl nusxasi.

Nega biz ishtirok etishga qaror qildik?

Muxtasar qilib aytganda, bizning mutaxassisligimiz nostandart va murakkab loyihalar bo'lib, maxsus ko'nikmalarni talab qiladi va ko'pincha IT hamjamiyatiga ilmiy ahamiyatga ega. Biz ochiq manbalarni rivojlantirishni qat'iy qo'llab-quvvatlaymiz va uni ommalashtirish bilan shug'ullanamiz, shuningdek, kompyuter fanlari va matematika sohasida Rossiyaning etakchi universitetlari bilan hamkorlik qilamiz.

Tanlovning qiziqarli topshiriqlari va sevimli Telegram loyihamizdagi ishtiroki o‘z-o‘zidan ajoyib motivatsiya bo‘lgan bo‘lsa-da, sovrin jamg‘armasi qo‘shimcha rag‘batga aylandi. 🙂

TON blokcheyn tadqiqoti

Biz blokcheyn, sun'iy intellekt va mashinani o'rganish sohasidagi yangi ishlanmalarni diqqat bilan kuzatib boramiz va biz ishlayotgan har bir sohada bitta muhim nashrni o'tkazib yubormaslikka harakat qilamiz. Shunday ekan, musobaqa boshlanganda jamoamiz g'oyalari bilan tanish bo'lgan edi TON oq qog'oz. Biroq, TON bilan ishlashni boshlashdan oldin, biz texnik hujjatlarni va platformaning haqiqiy manba kodini tahlil qilmadik, shuning uchun birinchi qadam juda aniq edi - rasmiy hujjatlarni to'liq o'rganish. сайт va loyiha omborlari.

Tanlov boshlanganda kod allaqachon chop etilgan edi, shuning uchun vaqtni tejash uchun biz yozgan qo'llanma yoki xulosani izlashga qaror qildik. foydalanuvchilar tomonidan. Afsuski, bu hech qanday natija bermadi - platformani Ubuntu-da yig'ish bo'yicha ko'rsatmalardan tashqari, biz boshqa materiallarni topmadik.

Hujjatlarning o'zi yaxshi o'rganilgan, ammo ba'zi joylarda o'qish qiyin edi. Ko'pincha biz ma'lum fikrlarga qaytishga va mavhum g'oyalarning yuqori darajadagi tavsiflaridan past darajadagi amalga oshirish tafsilotlariga o'tishga majbur bo'ldik.

Agar spetsifikatsiyada amalga oshirishning batafsil tavsifi umuman bo'lmasa, osonroq bo'lar edi. Virtual mashina o'z stackini qanday ifodalashi haqidagi ma'lumotlar TON platformasi uchun aqlli shartnomalar yaratuvchi ishlab chiquvchilarni ularga yordam berishdan ko'ra chalg'itishi mumkin.

Nix: loyihani birlashtirish

Serokellda biz ashaddiy muxlislarmiz Nix. Biz u bilan loyihalarimizni yig'amiz va ularni ishlatamiz NixOps, va barcha serverlarimizga o'rnatilgan Nix OS. Shu tufayli, bizning barcha tuzilmalarimiz takrorlanadi va Nix o'rnatilishi mumkin bo'lgan har qanday operatsion tizimda ishlaydi.

Shunday qilib, biz yaratishdan boshladik TON yig'ish uchun ifoda bilan Nix qoplamasi. Uning yordami bilan TONni kompilyatsiya qilish imkon qadar sodda:

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

E'tibor bering, hech qanday bog'liqlikni o'rnatishingiz shart emas. Nix, NixOS, Ubuntu yoki macOS dan foydalansangiz ham, siz uchun sehrli tarzda hamma narsani qiladi.

TON uchun dasturlash

TON tarmog'idagi aqlli shartnoma kodi TON virtual mashinasida (TVM) ishlaydi. TVM boshqa virtual mashinalarga qaraganda ancha murakkab va juda qiziqarli funksiyalarga ega, masalan, u bilan ishlashi mumkin davomi и ma'lumotlarga havolalar.

Bundan tashqari, TON yigitlari uchta yangi dasturlash tilini yaratdilar:

Besh ga o'xshash universal stek dasturlash tilidir To'rtinchi. Uning super qobiliyati TVM bilan muloqot qilish qobiliyatidir.

FunC ga o'xshash aqlli kontrakt dasturlash tilidir C va boshqa tilga - Fift Assemblerga kompilyatsiya qilinadi.

Beshinchi Assembler — TVM uchun ikkilik bajariladigan kodni yaratish uchun beshta kutubxona. Beshinchi Assemblerda kompilyator yo'q. Bu O‘rnatilgan domenga xos til (eDSL).

Bizning musobaqamiz ishlaydi

Va nihoyat, bizning harakatlarimiz natijalarini ko'rish vaqti keldi.

Asinxron to'lov kanali

To'lov kanali - bu ikki foydalanuvchiga blokcheyndan tashqari to'lovlarni jo'natish imkonini beruvchi aqlli shartnoma. Natijada siz nafaqat pulni (komissiya yo'q), balki vaqtni ham tejaysiz (keyingi blokni qayta ishlashni kutishingiz shart emas). To'lovlar kerakli darajada kichik va kerak bo'lganda tez-tez bo'lishi mumkin. Bunday holda, tomonlar bir-biriga ishonishlari shart emas, chunki yakuniy kelishuvning adolatliligi aqlli shartnoma bilan kafolatlanadi.

Muammoning juda oddiy yechimini topdik. Ikki tomon imzolangan xabarlarni almashishi mumkin, ularning har biri ikkita raqamni o'z ichiga oladi - har bir tomon to'lagan to'liq miqdor. Bu ikki raqam shunday ishlaydi vektor soati an'anaviy taqsimlangan tizimlarda va tranzaktsiyalar bo'yicha "avval sodir bo'lgan" tartibini o'rnating. Ushbu ma'lumotlardan foydalanib, shartnoma har qanday mumkin bo'lgan nizolarni hal qila oladi.

Aslida, bu g'oyani amalga oshirish uchun bitta raqam kifoya qiladi, lekin biz ikkalasini ham qoldirdik, chunki bu orqali biz qulayroq foydalanuvchi interfeysini yaratishimiz mumkin edi. Bundan tashqari, biz har bir xabarda to'lov miqdorini kiritishga qaror qildik. Busiz, agar biron sababga ko'ra xabar yo'qolgan bo'lsa, unda barcha miqdorlar va yakuniy hisob-kitoblar to'g'ri bo'lsa-da, foydalanuvchi yo'qotishni sezmasligi mumkin.

Bizning fikrimizni sinab ko'rish uchun biz bunday oddiy va qisqacha to'lov kanali protokolidan foydalanish misollarini qidirdik. Ajablanarlisi shundaki, biz faqat ikkitasini topdik:

  1. tavsifi shunga o'xshash yondashuv, faqat bir yo'nalishli kanal uchun.
  2. Oʻquv qoʻllanma, bu bizniki bilan bir xil fikrni tasvirlaydi, lekin umumiy to'g'rilik va nizolarni hal qilish tartiblari kabi ko'plab muhim tafsilotlarni tushuntirmasdan.

Bayonnomamizning to‘g‘riligiga alohida e’tibor berib, uni batafsil bayon etish mantiqiy ekani ayon bo‘ldi. Bir necha takrorlashdan so'ng, spetsifikatsiya tayyor edi va endi siz ham qila olasiz. unga qarang.

Biz shartnomani FunC-da amalga oshirdik va tashkilotchilar tavsiya qilganidek, Fift-da shartnomamiz bilan o'zaro ishlash uchun buyruq qatori yordam dasturini yozdik. Biz CLI uchun istalgan boshqa tilni tanlashimiz mumkin edi, lekin biz Fit ilovasini amalda qanday ishlashini ko‘rishga qiziqdik.

Rostini aytsam, Fift bilan ishlaganimizdan so'ng, biz ushbu tilni ishlab chiqilgan vositalar va kutubxonalarga ega bo'lgan mashhur va faol ishlatiladigan tillardan afzal ko'rish uchun hech qanday jiddiy sabablarni ko'rmadik. Stekga asoslangan tilda dasturlash juda yoqimsiz, chunki siz doimo stekdagi narsalarni boshingizda saqlashingiz kerak va kompilyator bunga yordam bermaydi.

Shuning uchun, bizning fikrimizcha, Fift mavjudligining yagona asosi uning Fift Assembler uchun xost tili sifatidagi rolidir. Ammo bu yagona maqsad uchun yangisini ixtiro qilishdan ko'ra, TVM assemblerini mavjud tilga joylashtirish yaxshiroq emasmi?

TVM Haskell eDSL

Endi ikkinchi aqlli shartnomamiz haqida gapirish vaqti keldi. Biz ko'p imzoli hamyonni ishlab chiqishga qaror qildik, ammo FunC-da boshqa aqlli shartnoma yozish juda zerikarli bo'lar edi. Biz biroz lazzat qo'shmoqchi edik va bu TVM uchun o'zimizning montaj tilimiz edi.

Fift Assembler singari, bizning yangi tilimiz o'rnatilgan, ammo biz Fift o'rniga xost sifatida Haskellni tanladik, bu bizga uning ilg'or turdagi tizimidan to'liq foydalanish imkonini berdi. Aqlli kontraktlar bilan ishlashda, hatto kichik xatoning narxi juda yuqori bo'lishi mumkin bo'lsa, bizning fikrimizcha, statik yozish katta afzallik hisoblanadi.

Haskell-ga o'rnatilgan TVM assembler qanday ko'rinishini ko'rsatish uchun biz unga standart hamyonni kiritdik. Bu erda bir nechta narsalarga e'tibor berish kerak:

  • Ushbu shartnoma bitta funktsiyadan iborat, lekin siz xohlaganingizcha foydalanishingiz mumkin. Xost tilida yangi funktsiyani (masalan, Haskell) aniqlaganingizda, bizning eDSL sizga uni TVM-da alohida tartib bo'lishini yoki qo'ng'iroq nuqtasida oddiy qilib qo'yishni tanlash imkonini beradi.
  • Xaskell singari, funksiyalar kompilyatsiya vaqtida tekshiriladigan turlarga ega. Bizning eDSL-da funktsiyaning kirish turi funksiya kutgan stek turidir va natija turi qo'ng'iroqdan keyin ishlab chiqariladigan stek turidir.
  • Kodda izohlar mavjud stacktype, chaqiruv nuqtasida kutilgan stek turini tavsiflash. Asl hamyon shartnomasida bu shunchaki sharhlar edi, lekin bizning eDSL-da ular aslida kodning bir qismidir va kompilyatsiya vaqtida tekshiriladi. Agar kod o'zgarsa va stek turi o'zgarsa, ular ishlab chiquvchiga muammoni topishga yordam beradigan hujjat yoki bayonot sifatida xizmat qilishi mumkin. Albatta, bunday izohlar ish vaqtining ishlashiga ta'sir qilmaydi, chunki ular uchun TVM kodi yaratilmaydi.
  • Bu hali ikki hafta ichida yozilgan prototip, shuning uchun loyiha ustida hali ko'p ish qilish kerak. Misol uchun, quyidagi kodda ko'rgan sinflarning barcha namunalari avtomatik ravishda yaratilishi kerak.

Multisig hamyonni amalga oshirish bizning eDSL-da shunday ko'rinadi:

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

Bizning eDSL va ko'p imzoli hamyon shartnomamizning to'liq manba kodini quyidagi manzilda topishingiz mumkin bu ombor. Va yana batafsil aytib berdi o'rnatilgan tillar haqida, bizning hamkasbimiz Georgiy Agapov.

Raqobat va TON haqida xulosalar

Hammasi bo'lib bizning ishimiz 380 soat davom etdi (hujjatlar bilan tanishish, uchrashuvlar va haqiqiy ishlab chiqish). Tanlov loyihasida beshta ishlab chiquvchi ishtirok etdi: CTO, jamoa rahbari, blokcheyn platformasi mutaxassislari va Haskell dasturiy ta’minot ishlab chiquvchilari.

Biz tanlovda qiyinchiliksiz ishtirok etish uchun resurslarni topdik, chunki hakaton ruhi, yaqin jamoaviy ish va yangi texnologiyalar jihatlariga tezda sho‘ng‘ish zarurati har doim hayajonli. Cheklangan resurslar sharoitida maksimal natijalarga erishish uchun bir necha uyqusiz tunlar bebaho tajriba va ajoyib xotiralar bilan qoplanadi. Bundan tashqari, bunday vazifalar ustida ishlash har doim kompaniya jarayonlarining yaxshi sinovidir, chunki yaxshi ishlaydigan ichki o'zaro ta'sirsiz chinakam munosib natijalarga erishish juda qiyin.

Qo'shiq so'zlarini chetga surib qo'ying: biz TON jamoasi tomonidan qilingan mehnat miqdoridan hayratda qoldik. Ular murakkab, chiroyli va eng muhimi, ishlaydigan tizimni qurishga muvaffaq bo'lishdi. TON katta salohiyatga ega platforma ekanligini isbotladi. Biroq, ushbu ekotizimning rivojlanishi uchun blokcheyn loyihalarida foydalanish nuqtai nazaridan ham, rivojlanish vositalarini takomillashtirish nuqtai nazaridan ham ko'p narsalarni qilish kerak. Biz hozir ushbu jarayonning bir qismi ekanligimizdan faxrlanamiz.

Agar ushbu maqolani o'qib chiqqaningizdan keyin sizda hali ham savollaringiz yoki muammolaringizni hal qilish uchun TONdan qanday foydalanish haqida fikringiz bo'lsa, bizga yozing — tajribamizni baham ko‘rishdan xursand bo‘lamiz.

Manba: www.habr.com

a Izoh qo'shish