Хаскеллтэй хамтран FunC-г FunCtional болгон хувиргах нь: Серокелл Telegram Blockchain тэмцээнд хэрхэн түрүүлсэн.

Та тэр Telegram-ыг сонссон байх Ton blockchain платформыг эхлүүлэх гэж байна. Гэхдээ та саяхан Telegram-ийн мэдээг орхигдуулж магадгүй юм уралдаан зарлалаа энэ платформд зориулсан нэг буюу хэд хэдэн ухаалаг гэрээг хэрэгжүүлэхэд зориулагдсан.

Томоохон блокчэйн төслүүдийг хөгжүүлэх арвин туршлагатай Серокелл багийнхан хажуугаар нь зогсож чадсангүй. Бид таван ажилтныг тэмцээнд илгээсэн бөгөөд хоёр долоо хоногийн дараа тэд Sexy Chameleon гэсэн даруухан санамсаргүй хочоор тэргүүн байр эзэллээ. Энэ нийтлэлд би тэд үүнийг хэрхэн хийсэн талаар ярих болно. Ойрын арван минутын дотор та ядаж нэг сонирхолтой түүхийг уншиж, хамгийн ихдээ түүнээс ажилдаа хэрэг болохуйц хэрэгтэй зүйлийг олж авна гэж найдаж байна.

Гэхдээ бага зэрэг контекстээс эхэлье.

Өрсөлдөөн ба түүний нөхцөл

Тиймээс оролцогчдын гол ажил бол санал болгож буй нэг буюу хэд хэдэн ухаалаг гэрээг хэрэгжүүлэх, мөн TON экосистемийг сайжруулах санал гаргах явдал байв. Тэмцээн есдүгээр сарын 24-нөөс аравдугаар сарын 15-ныг хүртэл үргэлжилсэн бөгөөд зөвхөн арваннэгдүгээр сарын 15-нд дүнгээ гаргалаа. Энэ хугацаанд Telegram нь Telegram дахь VoIP дуудлагын чанарыг турших, үнэлэх зорилгоор C++ хэл дээр программ зохиох, хөгжүүлэх уралдааныг зохион байгуулж, үр дүнгээ зарлаж чадсаныг бодвол нэлээд удаж байна.

Зохион байгуулагчдын санал болгосон жагсаалтаас бид хоёр ухаалаг гэрээг сонгосон. Тэдгээрийн нэгд нь бид TON-д тараасан хэрэгслүүдийг ашигласан бол хоёр дахь нь манай инженерүүдийн тусгайлан TON-д зориулан боловсруулж, Haskell-д суулгасан шинэ хэлээр хэрэгжсэн.

Функциональ програмчлалын хэлийг сонгох нь санамсаргүй хэрэг биш юм. Д манай корпорацийн блог Функциональ хэлнүүдийн нарийн төвөгтэй байдал нь яагаад хэтрүүлэг гэж боддог, яагаад тэдгээрийг объектод чиглэсэн хэлнээс илүүд үздэг талаар бид ихэвчлэн ярьдаг. Дашрамд хэлэхэд энэ нь бас агуулдаг энэ нийтлэлийн эх.

Бид яагаад оролцохоор шийдсэн бэ?

Товчхондоо, бидний мэргэшил бол тусгай ур чадвар шаарддаг стандарт бус, нарийн төвөгтэй төслүүд бөгөөд мэдээллийн технологийн нийгэмлэгийн хувьд ихэвчлэн шинжлэх ухааны үнэ цэнэтэй байдаг. Бид нээлттэй эхийн хөгжлийг тууштай дэмжиж, түүнийг сурталчлах, мөн Оросын тэргүүлэх их дээд сургуулиудтай компьютерийн шинжлэх ухаан, математикийн чиглэлээр хамтран ажилладаг.

Тэмцээний сонирхолтой даалгавар, бидний хайртай Telegram төсөлд оролцох нь өөрөө маш сайн урам зориг байсан боловч шагналын сан нь нэмэлт урамшуулал болсон. 🙂

TON блокчейн судалгаа

Бид блокчэйн, хиймэл оюун ухаан, машин сургалтын шинэ бүтээн байгуулалтыг анхааралтай ажиглаж, ажиллаж буй салбар бүртээ нэг чухал хувилбарыг алдахгүй байхыг хичээдэг. Тиймээс тэмцээн эхлэхэд манай баг аль хэдийн санаануудыг мэддэг байсан TON цагаан цаас. Гэсэн хэдий ч TON-той ажиллахаасаа өмнө бид техникийн баримт бичиг, платформын бодит эх кодыг шинжилээгүй тул эхний алхам нь тодорхой байсан - албан ёсны баримт бичгийг сайтар судлах. сайт болон дотор төслийн агуулахууд.

Тэмцээн эхлэх үед код нь аль хэдийн хэвлэгдсэн байсан тул цаг хэмнэхийн тулд бид гарын авлага эсвэл тоймыг хайж олохоор шийдсэн. хэрэглэгчид. Харамсалтай нь энэ нь ямар ч үр дүнд хүрээгүй - Ubuntu дээр платформ угсрах заавраас гадна бидэнд өөр материал олдсонгүй.

Баримт бичгийг өөрөө сайтар судалсан боловч зарим хэсэгт уншихад хэцүү байсан. Ихэнхдээ бид тодорхой цэгүүд рүү буцаж, хийсвэр санааны өндөр түвшний тайлбараас доод түвшний хэрэгжилтийн дэлгэрэнгүй мэдээлэл рүү шилжих шаардлагатай болдог.

Тодорхойлолтод хэрэгжилтийн дэлгэрэнгүй тайлбарыг огт оруулаагүй бол илүү хялбар байх болно. Виртуал машин нь стекийг хэрхэн төлөөлдөг тухай мэдээлэл нь TON платформд зориулж ухаалаг гэрээ байгуулж буй хөгжүүлэгчдэд туслахаас илүү анхаарал сарниулдаг.

Никс: төслийг нэгтгэж байна

Серокеллд бид том шүтэн бишрэгчид Ник. Бид үүнтэй хамт төслүүдээ цуглуулж, тэдгээрийг ашиглан байрлуулдаг NixOps, мөн манай бүх сервер дээр суулгасан Nix үйлдлийн систем. Үүний ачаар манай бүх бүтээцүүд дахин давтагдах боломжтой бөгөөд Nix суулгаж болох дурын үйлдлийн систем дээр ажилладаг.

Тиймээс бид бүтээж эхэлсэн TON угсралтын илэрхийлэл бүхий Nix давхарга. Түүний тусламжтайгаар TON эмхэтгэх нь аль болох хялбар юм.

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

Та ямар нэгэн хамаарлыг суулгах шаардлагагүй гэдгийг анхаарна уу. Nix, та NixOS, Ubuntu эсвэл macOS ашиглаж байгаа эсэхээс үл хамааран таны төлөө бүх зүйлийг ид шидээр хийх болно.

TON-д зориулсан програмчлал

TON сүлжээн дэх ухаалаг гэрээний код нь TON Virtual Machine (TVM) дээр ажилладаг. TVM нь бусад виртуал машинуудаас илүү төвөгтэй бөгөөд маш сонирхолтой функцтэй, жишээлбэл, түүнтэй ажиллах боломжтой үргэлжлэлүүд и өгөгдөлтэй холбох.

Түүгээр ч зогсохгүй TON-ийн залуус гурван шинэ програмчлалын хэлийг бүтээжээ.

тав төстэй бүх нийтийн стек програмчлалын хэл юм Урьдчилгаа. Түүний супер чадвар бол TVM-тэй харилцах чадвар юм.

FunC -тэй төстэй ухаалаг гэрээний програмчлалын хэл юм C бөгөөд өөр хэл дээр эмхэтгэсэн - Fift Assembler.

Тав дахь Ассемблер — TVM-д зориулсан хоёртын гүйцэтгэх код үүсгэх таван номын сан. Тавдугаар Ассемблерт хөрвүүлэгч байхгүй. Энэ Embedded Domain Specific Language (eDSL).

Манай уралдаан амжилттай болж байна

Эцэст нь хэлэхэд бидний хүчин чармайлтын үр дүнг харах цаг болжээ.

Асинхрон төлбөрийн суваг

Төлбөрийн суваг нь хоёр хэрэглэгчдэд блокчэйноос гадуур төлбөр илгээх боломжийг олгодог ухаалаг гэрээ юм. Үүний үр дүнд та зөвхөн мөнгө төдийгүй цаг хугацаа хэмнэнэ (дараагийн блок боловсруулагдахыг хүлээх шаардлагагүй). Төлбөр нь хүссэн хэмжээгээрээ бага, шаардлагатай давтамжтай байж болно. Энэ тохиолдолд эцсийн тооцооны шударга байдал нь ухаалаг гэрээгээр баталгаажсан тул талууд бие биедээ итгэх шаардлагагүй болно.

Бид асуудлын нэлээд энгийн шийдлийг олсон. Хоёр тал гарын үсэг зурсан мессежийг солилцож болно, тус бүр нь хоёр дугаарыг агуулсан - тал бүрийн төлсөн бүрэн хэмжээний. Энэ хоёр тоо нь адилхан ажилладаг вектор цаг уламжлалт тархсан системд гүйлгээний "өмнө нь тохиолдсон" дарааллыг тогтооно. Энэхүү өгөгдлийг ашиглан гэрээ нь аливаа зөрчилдөөнийг шийдвэрлэх боломжтой болно.

Үнэн хэрэгтээ энэ санааг хэрэгжүүлэхэд нэг тоо хангалттай, гэхдээ энэ нь хэрэглэгчийн интерфэйсийг илүү тохиромжтой болгохын тулд хоёуланг нь орхисон. Үүнээс гадна бид мессеж бүрт төлбөрийн хэмжээг оруулахаар шийдсэн. Үүнгүйгээр, хэрэв ямар нэг шалтгаанаар мессеж алдагдсан бол бүх дүн, эцсийн тооцоолол зөв байх боловч хэрэглэгч алдагдлыг анзаарахгүй байж магадгүй юм.

Бидний санааг шалгахын тулд бид ийм энгийн бөгөөд товч төлбөрийн сувгийн протоколыг ашиглах жишээг хайсан. Гайхалтай нь бид хоёрыг л олсон:

  1. Тайлбар ижил төстэй арга, зөвхөн нэг чиглэлтэй сувгийн хувьд.
  2. Заавар, энэ нь биднийхтэй ижил санааг тодорхойлсон боловч ерөнхий зөв байдал, зөрчилдөөнийг шийдвэрлэх журам гэх мэт олон чухал нарийн ширийн зүйлийг тайлбарлаагүй болно.

Протоколынхоо зөв байдалд онцгой анхаарал хандуулж, нарийвчлан тайлбарлах нь ойлгомжтой болсон. Хэд хэдэн давталтын дараа техникийн үзүүлэлтүүд бэлэн болсон бөгөөд одоо та ч бас чадна. түүн рүү хар.

Бид гэрээг FunC-д хэрэгжүүлсэн бөгөөд зохион байгуулагчдын зөвлөсний дагуу бид гэрээтэйгээ бүрэн харьцах командын мөрийн хэрэглүүрийг Fift дээр бичсэн. Бид CLI-дээ өөр ямар ч хэл сонгож болох байсан ч практикт хэрхэн ажиллаж байгааг харахын тулд Фитийг туршиж үзэх сонирхолтой байсан.

Үнэнийг хэлэхэд, Fift-тай ажилласны дараа бид энэ хэлийг хөгжүүлсэн хэрэгсэл, номын сан бүхий алдартай, идэвхтэй ашигладаг хэлнүүдээс илүүд үзэх ямар ч үндэслэлтэй шалтгааныг олж хараагүй. Стек дээр суурилсан хэлээр програмчлах нь маш тааламжгүй байдаг, учир нь та стек дээр байгаа зүйлийг байнга толгойдоо байлгах хэрэгтэй бөгөөд хөрвүүлэгч үүнд тус болохгүй.

Тиймээс бидний бодлоор Fift-ийн оршин тогтнох цорын ганц үндэслэл бол Fift Assembler-ийн хост хэлний үүрэг юм. Гэхдээ энэ үндсэн зорилгод зориулж шинийг зохион бүтээхийн оронд TVM ассемблерийг одоо байгаа хэл рүү оруулах нь дээр биш гэж үү?

TVM Haskell eDSL

Одоо хоёр дахь ухаалаг гэрээний талаар ярих цаг болжээ. Бид олон гарын үсэг бүхий хэтэвч боловсруулахаар шийдсэн боловч FunC дээр өөр ухаалаг гэрээ бичих нь хэтэрхий уйтгартай байх болно. Бид зарим нэг амтыг нэмэхийг хүссэн бөгөөд энэ нь бидний TVM-ийн ассемблер хэл байсан.

Fift Assembler-ийн нэгэн адил манай шинэ хэлийг суулгасан боловч бид Fift-ийн оронд Haskell-ийг хостоор сонгосон нь түүний дэвшилтэт төрлийн системийг бүрэн ашиглах боломжийг бидэнд олгосон. Ухаалаг гэрээнүүдтэй ажиллахдаа жижиг алдааны зардал маш өндөр байдаг бол бидний бодлоор статик бичих нь том давуу тал юм.

Haskell-д суулгасан TVM ассемблер ямар харагддагийг харуулахын тулд бид түүн дээр стандарт түрийвчийг хэрэгжүүлсэн. Энд анхаарах хэдэн зүйл байна:

  • Энэхүү гэрээ нь нэг функцээс бүрдэх боловч та хүссэн хэмжээгээрээ ашиглах боломжтой. Та хост хэл дээр (жишээ нь Haskell) шинэ функцийг тодорхойлоход манай eDSL нь үүнийг TVM-д тусдаа горим болгох уу эсвэл дуудлага хийх цэг дээр энгийн байдлаар оруулах уу гэдгээ сонгох боломжийг танд олгоно.
  • Хаскелл шиг функцууд нь хөрвүүлэх үед шалгадаг төрлүүдтэй байдаг. Манай eDSL-д функцийн оролтын төрөл нь функцийн хүлээж буй стекийн төрөл бөгөөд үр дүнгийн төрөл нь дуудлагын дараа үүсэх стекийн төрөл юм.
  • Код нь тайлбартай stacktype, дуудлагын цэг дээр хүлээгдэж буй стекийн төрлийг дүрсэлсэн. Анхны түрийвчний гэрээнд эдгээр нь зүгээр л тайлбар байсан боловч манай eDSL-д тэдгээр нь үнэндээ кодын нэг хэсэг бөгөөд эмхэтгэх үед шалгадаг. Эдгээр нь код өөрчлөгдөж, стекийн төрөл өөрчлөгдсөн тохиолдолд хөгжүүлэгчид асуудлыг олоход туслах баримт бичиг эсвэл мэдэгдэл болж болно. Мэдээжийн хэрэг, ийм тэмдэглэгээ нь ажиллах үеийн гүйцэтгэлд нөлөөлөхгүй, учир нь тэдэнд зориулж TVM код үүсгэдэггүй.
  • Энэ нь хоёр долоо хоногийн дотор бичигдсэн прототип хэвээр байгаа тул төсөл дээр хийх ажил их байна. Жишээлбэл, таны доорх кодонд байгаа ангиудын бүх тохиолдлууд автоматаар үүсгэгдэх ёстой.

Манай eDSL дээр multisig түрийвчний хэрэгжилт иймэрхүү харагдаж байна.

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

Манай eDSL болон олон гарын үсгийн хэтэвчний гэрээний бүрэн эх кодыг эндээс авах боломжтой энэ агуулах. Мөн бусад дэлгэрэнгүй хэлсэн суурилуулсан хэлний тухай, манай хамтрагч Георгий Агапов.

Тэмцээний талаархи дүгнэлт, ТОН

Нийтдээ бидний ажилд 380 цаг зарцуулсан (баримт бичигтэй танилцах, уулзалт хийх, бодит боловсруулалт зэрэг). Тэмцээний төсөлд таван хөгжүүлэгч оролцсон: CTO, багийн ахлагч, блокчейн платформын мэргэжилтнүүд болон Haskell програм хангамж хөгжүүлэгчид.

Хакатон, нягт багаар ажиллах, шинэ технологийн тал дээр хурдан шимтэх хэрэгцээ үргэлж сэтгэл хөдөлгөм байдаг тул бид тэмцээнд оролцох нөөцийг ямар ч бэрхшээлгүйгээр олсон. Хязгаарлагдмал нөөцийн нөхцөлд хамгийн их үр дүнд хүрэхийн тулд хэд хэдэн нойргүй хоносон нь үнэлж баршгүй туршлага, гайхалтай дурсамжаар нөхөгддөг. Нэмж дурдахад, ийм даалгавар дээр ажиллах нь компанийн үйл явцын сайн шалгуур болдог, учир нь дотоод харилцан үйлчлэл сайн байхгүйгээр үнэхээр зохистой үр дүнд хүрэх нь туйлын хэцүү байдаг.

Дууны үгийг хажуугаар нь дурдвал: ТОН-ын багийн хийсэн ажил бидэнд маш их сэтгэгдэл төрүүлсэн. Тэд нарийн төвөгтэй, үзэсгэлэнтэй, хамгийн чухал нь ажлын системийг байгуулж чадсан. TON бол асар их боломж бүхий платформ гэдгээ баталсан. Гэсэн хэдий ч энэхүү экосистемийг хөгжүүлэхийн тулд блокчэйн төслүүдэд ашиглах, хөгжлийн хэрэгслүүдийг сайжруулах тал дээр илүү их зүйлийг хийх шаардлагатай байна. Бид одоо энэ үйл явцын нэг хэсэг болсондоо бахархаж байна.

Хэрэв энэ нийтлэлийг уншсаны дараа танд ямар нэгэн асуулт байгаа бол эсвэл TON-г ашиглан асуудлаа хэрхэн шийдвэрлэх талаар санаа байвал, бидэнд бичээрэй - Бид туршлагаа хуваалцахдаа баяртай байх болно.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх