Mengubah FunC menjadi FunCtional dengan Haskell: Bagaimana Serokell memenangi Pertandingan Rantaian Blok Telegram

Anda mungkin pernah mendengar Telegram itu akan melancarkan platform blockchain Ton. Tetapi anda mungkin terlepas berita yang tidak lama dahulu Telegram mengumumkan pertandingan untuk pelaksanaan satu atau lebih kontrak pintar untuk platform ini.

Pasukan Serokell, dengan pengalaman yang luas dalam membangunkan projek blockchain yang besar, tidak boleh diketepikan. Kami mewakilkan lima pekerja untuk pertandingan itu, dan dua minggu kemudian mereka mendapat tempat pertama di bawah (dalam) nama samaran rawak Sexy Chameleon. Dalam artikel ini saya akan bercakap tentang bagaimana mereka melakukannya. Kami berharap dalam sepuluh minit akan datang anda sekurang-kurangnya akan membaca cerita yang menarik, dan paling banyak anda akan mendapati sesuatu yang berguna di dalamnya yang boleh anda gunakan dalam kerja anda.

Tetapi mari kita mulakan dengan sedikit konteks.

Persaingan dan syarat-syaratnya

Jadi, tugas utama peserta adalah pelaksanaan satu atau lebih kontrak pintar yang dicadangkan, serta membuat cadangan untuk menambah baik ekosistem TON. Pertandingan ini berlangsung dari 24 September hingga 15 Oktober, dan keputusan diumumkan hanya pada 15 November. Agak lama, memandangkan selama ini Telegram berjaya mengadakan dan mengumumkan keputusan peraduan reka bentuk dan pembangunan aplikasi dalam C++ untuk menguji dan menilai kualiti panggilan VoIP di Telegram.

Kami memilih dua kontrak pintar daripada senarai yang dicadangkan oleh penganjur. Untuk salah satu daripada mereka, kami menggunakan alat yang diedarkan dengan TON, dan yang kedua telah dilaksanakan dalam bahasa baharu yang dibangunkan oleh jurutera kami khusus untuk TON dan dibina ke dalam Haskell.

Pemilihan bahasa pengaturcaraan berfungsi bukanlah secara kebetulan. Dalam kami blog korporat Kami sering bercakap tentang mengapa kami menganggap kerumitan bahasa berfungsi adalah keterlaluan yang besar dan mengapa kami biasanya lebih suka bahasa itu daripada bahasa berorientasikan objek. By the way, ia juga mengandungi asal artikel ini.

Mengapa kami memutuskan untuk mengambil bahagian?

Ringkasnya, kerana pengkhususan kami adalah projek tidak standard dan kompleks yang memerlukan kemahiran khas dan selalunya bernilai saintifik kepada komuniti IT. Kami sangat menyokong pembangunan sumber terbuka dan terlibat dalam mempopularkannya, dan juga bekerjasama dengan universiti terkemuka Rusia dalam bidang sains komputer dan matematik.

Tugasan menarik dalam pertandingan dan penglibatan dalam projek Telegram tercinta kami sendiri merupakan motivasi yang sangat baik, tetapi dana hadiah menjadi insentif tambahan. πŸ™‚

Penyelidikan blockchain TON

Kami memantau dengan teliti perkembangan baharu dalam rantaian blok, kecerdasan buatan dan pembelajaran mesin dan cuba untuk tidak terlepas satu keluaran penting dalam setiap bidang di mana kami bekerja. Oleh itu, pada masa pertandingan bermula, pasukan kami sudah biasa dengan idea daripada TON kertas putih. Walau bagaimanapun, sebelum memulakan kerja dengan TON, kami tidak menganalisis dokumentasi teknikal dan kod sumber sebenar platform, jadi langkah pertama agak jelas - kajian menyeluruh dokumentasi rasmi mengenai Online dan repositori projek.

Pada masa pertandingan bermula, kod telah pun diterbitkan, jadi untuk menjimatkan masa, kami memutuskan untuk mencari panduan atau ringkasan yang ditulis oleh oleh pengguna. Malangnya, ini tidak memberikan apa-apa hasil - selain daripada arahan untuk memasang platform pada Ubuntu, kami tidak menemui sebarang bahan lain.

Dokumentasi itu sendiri telah dikaji dengan baik, tetapi sukar dibaca di beberapa kawasan. Selalunya kami terpaksa kembali ke perkara tertentu dan beralih daripada penerangan peringkat tinggi idea abstrak kepada butiran pelaksanaan peringkat rendah.

Ia akan menjadi lebih mudah jika spesifikasi tidak termasuk penerangan terperinci pelaksanaan sama sekali. Maklumat tentang cara mesin maya mewakili susunannya lebih berkemungkinan mengalih perhatian pembangun yang mencipta kontrak pintar untuk platform TON daripada membantu mereka.

Nix: meletakkan projek bersama-sama

Di Serokel kami peminat tegar Nix. Kami mengumpul projek kami dengannya dan menggunakan projek tersebut NixOps, dan dipasang pada semua pelayan kami OS Nix. Terima kasih kepada ini, semua binaan kami boleh dihasilkan semula dan berfungsi pada mana-mana sistem pengendalian yang Nix boleh dipasang.

Jadi kami mulakan dengan mencipta Tindanan Nix dengan ungkapan untuk pemasangan TON. Dengan bantuannya, menyusun TON semudah mungkin:

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

Ambil perhatian bahawa anda tidak perlu memasang sebarang kebergantungan. Nix akan melakukan segala-galanya untuk anda, sama ada anda menggunakan NixOS, Ubuntu atau macOS.

Pengaturcaraan untuk TON

Kod kontrak pintar dalam Rangkaian TON berjalan pada Mesin Maya (TVM) TON. TVM adalah lebih kompleks daripada kebanyakan mesin maya lain, dan mempunyai fungsi yang sangat menarik, contohnya, ia boleh berfungsi dengannya sambungan ΠΈ pautan kepada data.

Selain itu, lelaki dari TON mencipta tiga bahasa pengaturcaraan baharu:

Lima ialah bahasa pengaturcaraan tindanan universal yang menyerupai Forth. Super ability dia ialah kebolehan berinteraksi dengan TVM.

KeseronokanC ialah bahasa pengaturcaraan kontrak pintar yang serupa dengan C dan disusun ke dalam bahasa lain - Fift Assembler.

Penghimpun Kelima β€” Lima perpustakaan untuk menjana kod boleh laku binari untuk TVM. Fifth Assembler tidak mempunyai penyusun. ini Bahasa Khusus Domain Terbenam (eDSL).

Persaingan kami berfungsi

Akhirnya, tiba masanya untuk melihat hasil usaha kita.

Saluran pembayaran tak segerak

Saluran pembayaran ialah kontrak pintar yang membolehkan dua pengguna menghantar pembayaran di luar rantaian blok. Akibatnya, anda bukan sahaja menjimatkan wang (tiada komisen), tetapi juga masa (anda tidak perlu menunggu blok seterusnya diproses). Bayaran boleh sekecil yang dikehendaki dan sekerap yang diperlukan. Dalam kes ini, pihak-pihak tidak perlu mempercayai satu sama lain, kerana keadilan penyelesaian akhir dijamin oleh kontrak pintar.

Kami menemui penyelesaian yang agak mudah untuk masalah itu. Dua pihak boleh bertukar-tukar mesej yang ditandatangani, setiap satu mengandungi dua nomborβ€”jumlah penuh yang dibayar oleh setiap pihak. Kedua-dua nombor ini berfungsi seperti jam vektor dalam sistem pengedaran tradisional dan tetapkan pesanan "berlaku sebelum" pada urus niaga. Menggunakan data ini, kontrak akan dapat menyelesaikan sebarang kemungkinan konflik.

Malah, satu nombor sudah cukup untuk melaksanakan idea ini, tetapi kami meninggalkan kedua-duanya kerana dengan cara ini kami boleh membuat antara muka pengguna yang lebih mudah. Selain itu, kami memutuskan untuk memasukkan jumlah pembayaran dalam setiap mesej. Tanpa itu, jika mesej itu hilang atas sebab tertentu, maka, walaupun semua jumlah dan pengiraan akhir akan betul, pengguna mungkin tidak menyedari kehilangan itu.

Untuk menguji idea kami, kami mencari contoh penggunaan protokol saluran pembayaran yang ringkas dan padat. Yang menghairankan, kami mendapati hanya dua:

  1. ОписаниС pendekatan yang serupa, hanya untuk kes saluran satu arah.
  2. Tutorial, yang menerangkan idea yang sama seperti idea kami, tetapi tanpa menjelaskan banyak butiran penting, seperti ketepatan umum dan prosedur penyelesaian konflik.

Ia menjadi jelas bahawa masuk akal untuk menerangkan protokol kami secara terperinci, memberi perhatian khusus kepada ketepatannya. Selepas beberapa lelaran, spesifikasi telah sedia, dan kini anda juga boleh. Lihat dia.

Kami melaksanakan kontrak dalam FunC, dan kami menulis utiliti baris arahan untuk berinteraksi dengan kontrak kami sepenuhnya dalam Fift, seperti yang disyorkan oleh penganjur. Kami boleh memilih mana-mana bahasa lain untuk CLI kami, tetapi kami berminat untuk mencuba Fit untuk melihat prestasinya dalam amalan.

Sejujurnya, selepas bekerja dengan Fift, kami tidak melihat apa-apa sebab yang menarik untuk memilih bahasa ini daripada bahasa yang popular dan digunakan secara aktif dengan alatan dan perpustakaan yang dibangunkan. Pengaturcaraan dalam bahasa berasaskan timbunan agak tidak menyenangkan, kerana anda perlu sentiasa menyimpan dalam kepala anda apa yang ada pada timbunan, dan pengkompil tidak membantu dengan ini.

Oleh itu, pada pendapat kami, satu-satunya justifikasi untuk kewujudan Fift adalah peranannya sebagai bahasa hos untuk Fift Assembler. Tetapi bukankah lebih baik untuk membenamkan pemasang TVM ke dalam beberapa bahasa sedia ada, daripada mencipta bahasa baharu untuk tujuan ini?

TVM Haskell eDSL

Kini tiba masanya untuk bercakap tentang kontrak pintar kedua kami. Kami memutuskan untuk membangunkan dompet berbilang tandatangan, tetapi menulis kontrak pintar lain dalam FunC akan menjadi terlalu membosankan. Kami ingin menambah sedikit rasa, dan itu adalah bahasa perhimpunan kami sendiri untuk TVM.

Seperti Fift Assembler, bahasa baharu kami dibenamkan, tetapi kami memilih Haskell sebagai hos dan bukannya Fift, membolehkan kami memanfaatkan sepenuhnya sistem jenis lanjutannya. Apabila bekerja dengan kontrak pintar, di mana kos walaupun ralat kecil boleh menjadi sangat tinggi, menaip statik, pada pendapat kami, adalah kelebihan yang besar.

Untuk menunjukkan rupa pemasang TVM yang dibenamkan dalam Haskell, kami melaksanakan dompet standard padanya. Berikut adalah beberapa perkara yang perlu diberi perhatian:

  • Kontrak ini terdiri daripada satu fungsi, tetapi anda boleh menggunakan seberapa banyak yang anda suka. Apabila anda mentakrifkan fungsi baharu dalam bahasa hos (iaitu Haskell), eDSL kami membolehkan anda memilih sama ada anda mahu ia menjadi rutin yang berasingan dalam TVM atau hanya sebaris pada titik panggilan.
  • Seperti Haskell, fungsi mempunyai jenis yang diperiksa pada masa penyusunan. Dalam eDSL kami, jenis input fungsi ialah jenis tindanan yang diharapkan oleh fungsi dan jenis hasil ialah jenis tindanan yang akan dihasilkan selepas panggilan.
  • Kod tersebut mempunyai anotasi stacktype, menerangkan jenis tindanan yang dijangkakan pada titik panggilan. Dalam kontrak dompet asal, ini hanyalah ulasan, tetapi dalam eDSL kami, ia sebenarnya sebahagian daripada kod dan disemak pada masa penyusunan. Ia boleh berfungsi sebagai dokumentasi atau pernyataan yang membantu pembangun mencari masalah jika kod berubah dan jenis tindanan berubah. Sudah tentu, anotasi sedemikian tidak memberi kesan kepada prestasi masa jalan, kerana tiada kod TVM dihasilkan untuknya.
  • Ini masih merupakan prototaip yang ditulis dalam masa dua minggu, jadi masih banyak kerja yang perlu dilakukan untuk projek itu. Sebagai contoh, semua kejadian kelas yang anda lihat dalam kod di bawah harus dijana secara automatik.

Beginilah rupa pelaksanaan dompet multisig pada eDSL kami:

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

Kod sumber penuh kontrak eDSL dan dompet berbilang tandatangan kami boleh didapati di repositori ini. Dan banyak lagi diceritakan secara terperinci mengenai bahasa terbina dalam, rakan sekerja kami Georgy Agapov.

Kesimpulan tentang pertandingan dan TON

Secara keseluruhan, kerja kami mengambil masa 380 jam (termasuk membiasakan diri dengan dokumentasi, mesyuarat dan pembangunan sebenar). Lima pembangun mengambil bahagian dalam projek pertandingan: CTO, ketua pasukan, pakar platform blockchain dan pembangun perisian Haskell.

Kami menemui sumber untuk menyertai peraduan tanpa kesukaran, memandangkan semangat hackathon, kerja berpasukan yang rapat dan keperluan untuk menyelami aspek teknologi baharu dengan cepat sentiasa mengujakan. Beberapa malam tanpa tidur untuk mencapai hasil maksimum dalam keadaan sumber yang terhad diberi pampasan oleh pengalaman yang tidak ternilai dan kenangan yang sangat baik. Di samping itu, menjalankan tugas sedemikian sentiasa menjadi ujian yang baik terhadap proses syarikat, kerana sangat sukar untuk mencapai hasil yang benar-benar baik tanpa interaksi dalaman yang berfungsi dengan baik.

Diketepikan lirik: kami kagum dengan jumlah kerja yang dilakukan oleh pasukan TON. Mereka berjaya membina sistem kerja yang kompleks, cantik, dan yang paling penting. TON telah membuktikan dirinya sebagai platform yang berpotensi besar. Walau bagaimanapun, untuk membolehkan ekosistem ini berkembang, banyak lagi yang perlu dilakukan, baik dari segi penggunaannya dalam projek blockchain dan dari segi menambah baik alat pembangunan. Kami berbangga kini menjadi sebahagian daripada proses ini.

Jika selepas membaca artikel ini anda masih mempunyai sebarang soalan atau mempunyai idea tentang cara menggunakan TON untuk menyelesaikan masalah anda, tulis kepada kami β€” kami akan gembira untuk berkongsi pengalaman kami.

Sumber: www.habr.com

Tambah komen