FunC-ті Хаскеллмен бірге FunCtional-ға айналдыру: Серокелл Telegram Blockchain байқауында қалай жеңіске жетті

Сіз бұл Telegram-ды естіген шығарсыз Ton блокчейн платформасын іске қоспақшы. Бірақ сіз Telegram-дағы жаңалықты жіберіп алған боларсыз байқау жариялады осы платформа үшін бір немесе бірнеше смарт келісімшарттарды жүзеге асыру үшін.

Ірі блокчейн жобаларын әзірлеуде үлкен тәжірибесі бар Serokell командасы шетте тұра алмады. Біз конкурсқа бес қызметкерді жібердік, ал екі аптадан кейін олар сексуалдық хамелеон қарапайым кездейсоқ лақап атымен бірінші орынды иеленді. Бұл мақалада мен олардың мұны қалай жасағаны туралы айтатын боламын. Алдағы он минут ішінде сіз ең болмағанда қызықты оқиғаны оқисыз және одан жұмысыңызда қолдануға болатын пайдалы нәрсе табасыз деп үміттенеміз.

Бірақ кішкене контекстен бастайық.

Бәсекелестік және оның шарттары

Сонымен, қатысушылардың негізгі міндеттері ұсынылған бір немесе бірнеше смарт келісімшарттарды жүзеге асыру, сондай-ақ TON экожүйесін жақсарту бойынша ұсыныстар жасау болды. Байқау 24 қыркүйек пен 15 қазан аралығында өтіп, қорытындысы 15 қарашада ғана белгілі болды. Осы уақыт ішінде Telegram Telegram-да VoIP қоңырауларының сапасын сынау және бағалау үшін C++ тілінде қосымшаларды әзірлеу және әзірлеу бойынша конкурстар өткізіп, нәтижелерін жариялағанын ескерсек, көп уақыт болды.

Ұйымдастырушылар ұсынған тізімнен екі смарт келісімшартты таңдадық. Олардың біреуі үшін біз TON арқылы таратылған құралдарды қолдандық, ал екіншісі біздің инженерлеріміз TON үшін арнайы әзірлеген және Хаскеллге енгізілген жаңа тілде жүзеге асырылды.

Функционалды бағдарламалау тілін таңдау кездейсоқ емес. Біздің корпоративтік блог Біз функционалдық тілдердің күрделілігі неге үлкен әсірелеу деп ойлайтынымыз және неге біз оларды объектіге бағытталған тілден артық көретініміз туралы жиі айтамыз. Айтпақшы, оның құрамында да бар осы мақаланың түпнұсқасы.

Неліктен біз қатысуға шешім қабылдадық?

Бір сөзбен айтқанда, біздің мамандануымыз ерекше дағдыларды қажет ететін және көбінесе IT қауымдастығы үшін ғылыми құндылыққа ие стандартты емес және күрделі жобалар болғандықтан. Біз ашық бастапқы кодты дамытуды қатты қолдаймыз және оны танымал етумен айналысамыз, сонымен қатар информатика және математика саласындағы жетекші ресейлік университеттермен ынтымақтасамыз.

Байқаудың қызықты тапсырмалары және біздің сүйікті Telegram жобамызға қатысу өз алдына тамаша мотивация болды, бірақ жүлде қоры қосымша ынталандыруға айналды. 🙂

TON блокчейнін зерттеу

Біз блокчейндегі, жасанды интеллекттегі және машиналық оқытудағы жаңа әзірлемелерді мұқият қадағалаймыз және біз жұмыс істейтін әр салада бірде-бір маңызды шығарылымды жіберіп алмауға тырысамыз. Сондықтан, байқау басталған кезде біздің команда идеялармен таныс болды TON ақ қағаз. Алайда, TON-мен жұмысты бастамас бұрын, біз техникалық құжаттаманы және платформаның нақты бастапқы кодын талдамадық, сондықтан бірінші қадам анық болды - ресми құжаттаманы мұқият зерделеу. сайт мен жоба репозиторийлері.

Байқау басталған кезде код жарияланып қойған болатын, сондықтан уақытты үнемдеу үшін біз жазған нұсқаулықты немесе түйіндемені іздеуді шештік. пайдаланушылар арқылы. Өкінішке орай, бұл нәтиже бермеді - платформаны Ubuntu-да құрастыру нұсқауларынан басқа, біз басқа материалдарды таппадық.

Құжаттаманың өзі жақсы зерттелді, бірақ кейбір жерлерде оқу қиын болды. Көбінесе біз белгілі бір нүктелерге оралуға және абстрактілі идеялардың жоғары деңгейлі сипаттамасынан төменгі деңгейдегі іске асыру бөлшектеріне ауысуға тура келді.

Егер спецификацияда іске асырудың толық сипаттамасы мүлдем болмаса, оңайырақ болар еді. Виртуалды машинаның стегін қалай көрсететіні туралы ақпарат TON платформасы үшін ақылды келісім-шарттар жасайтын әзірлеушілерге көмектесуден гөрі көбірек алаңдатады.

Nix: жобаны біріктіру

Серокелде біз үлкен жанкүйерлерміз Никс. Біз онымен жобаларымызды жинаймыз және оларды пайдаланамыз 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 пайдалансаңыз да, Nix сиқырлы түрде сіз үшін бәрін жасайды.

TON үшін бағдарламалау

TON желісіндегі смарт келісімшарт коды TON виртуалды машинасында (TVM) жұмыс істейді. TVM басқа виртуалды машиналарға қарағанда күрделірек және өте қызықты функционалдығы бар, мысалы, олармен жұмыс істей алады жалғасы и деректерге сілтемелер.

Сонымен қатар, TON жігіттері үш жаңа бағдарламалау тілін жасады:

Бес ұқсайтын әмбебап стек бағдарламалау тілі болып табылады Төртінші. Оның керемет қабілеті - TVM-мен өзара әрекеттесу қабілеті.

FunC ұқсас смарт келісімшарттық бағдарламалау тілі болып табылады C және басқа тілде құрастырылған - Fift Assembler.

Бесінші құрастырушы — TVM үшін екілік орындалатын кодты құруға арналған бес кітапхана. Бесінші Ассемблер компиляторы жоқ. Бұл Енгізілген доменге тән тіл (eDSL).

Сайысымыз жұмыс істейді

Ақырында, біздің күш-жігеріміздің нәтижесін қарастыратын кез келді.

Асинхронды төлем арнасы

Төлем арнасы - екі пайдаланушыға блокчейннен тыс төлемдерді жіберуге мүмкіндік беретін смарт келісімшарт. Нәтижесінде сіз ақшаны ғана емес (комиссия жоқ), сонымен қатар уақытты да үнемдейсіз (келесі блоктың өңделуін күтудің қажеті жоқ). Төлемдер қалағандай аз және қажет болғанша жиі болуы мүмкін. Бұл жағдайда тараптардың бір-біріне сенуінің қажеті жоқ, өйткені түпкілікті есеп айырысудың әділдігі ақылды келісім-шартпен қамтамасыз етіледі.

Біз мәселенің қарапайым шешімін таптық. Екі тарап қол қойылған хабарламаларды алмаса алады, олардың әрқайсысында екі нөмір бар — әр тарап төлеген толық сома. Бұл екі сан жұмыс істейді векторлық сағат дәстүрлі таратылған жүйелерде және транзакциялар бойынша «бұрын болған» тәртібін орнатыңыз. Осы деректерді пайдалана отырып, келісім-шарт кез келген ықтимал қайшылықты шеше алады.

Шындығында, бұл идеяны жүзеге асыру үшін бір сан жеткілікті, бірақ біз екеуін де қалдырдық, өйткені осылайша біз ыңғайлырақ пайдаланушы интерфейсін жасай аламыз. Сонымен қатар, біз әрбір хабарламаға төлем сомасын қосуды шештік. Онсыз, егер хабарлама қандай да бір себептермен жоғалса, онда барлық сомалар мен соңғы есептеу дұрыс болғанымен, пайдаланушы жоғалтуды байқамауы мүмкін.

Біздің идеямызды тексеру үшін біз осындай қарапайым және қысқа төлем арнасының хаттамасын пайдалану мысалдарын іздедік. Бір қызығы, біз тек екеуін таптық:

  1. сипаттамасы ұқсас тәсіл, тек бір бағытты арна жағдайында ғана.
  2. Оқулық, ол біздікімен бірдей идеяны сипаттайды, бірақ жалпы дұрыстық және жанжалдарды шешу процедуралары сияқты көптеген маңызды мәліметтерді түсіндірместен.

Хаттамамыздың дұрыстығына ерекше назар аудара отырып, оны егжей-тегжейлі сипаттаудың мағынасы бар екені белгілі болды. Бірнеше итерациядан кейін спецификация дайын болды, енді сіз де жасай аласыз. оған қара.

Біз келісім-шартты FunC жүйесінде іске асырдық және ұйымдастырушылар ұсынғандай, Fift-те келісім-шартпен әрекеттесу үшін пәрмен жолы утилитасын толығымен жаздық. Біз CLI үшін кез келген басқа тілді таңдай алар едік, бірақ оның іс жүзінде қалай орындалатынын көру үшін Fit қолданбасын қолданып көргіміз келді.

Шынымды айтсам, Fift-пен жұмыс істегеннен кейін біз бұл тілді танымал және дамыған құралдары мен кітапханалары бар белсенді қолданылатын тілдерден артық көрудің ешқандай дәлелді себептерін көрмедік. Стекке негізделген тілде бағдарламалау өте жағымсыз, өйткені сіз стекте не бар екенін үнемі басыңызда ұстауыңыз керек, ал компилятор бұған көмектеспейді.

Сондықтан, біздің ойымызша, Fift-тің бар болуының жалғыз дәлелі оның Fift Assembler үшін қабылдаушы тіл ретіндегі рөлі болып табылады. Бірақ бұл жалғыз мақсат үшін жаңасын ойлап тапқанша, TVM ассемблерін кейбір қолданыстағы тілге ендіру жақсы емес пе?

TVM Haskell eDSL

Енді екінші смарт келісімшартымыз туралы айтатын кез келді. Біз көп қолтаңбалы әмиян жасауды шештік, бірақ FunC-те басқа смарт келісім-шарт жазу тым қызық болар еді. Біз біраз дәм қосқымыз келді, бұл TVM үшін өзіміздің құрастыру тіліміз болды.

Fift Assembler сияқты, біздің жаңа тіліміз ендірілген, бірақ біз Fift-тің орнына Haskell-ті хост ретінде таңдадық, бұл бізге оның жетілдірілген типтік жүйесінің мүмкіндіктерін толық пайдалануға мүмкіндік берді. Кішкентай қатенің құны өте жоғары болатын смарт келісімшарттармен жұмыс істегенде, біздің ойымызша, статикалық теру үлкен артықшылық болып табылады.

TVM ассемблерінің Хаскеллге енгізілгенін көрсету үшін біз оған стандартты әмиянды енгіздік. Мұнда назар аудару керек бірнеше нәрсе бар:

  • Бұл келісімшарт бір функциядан тұрады, бірақ сіз қалағаныңызша пайдалана аласыз. Хост тілінде (яғни Haskell) жаңа функцияны анықтаған кезде, біздің eDSL сізге оның TVM бағдарламасында бөлек режим болуын немесе қоңырау шалу нүктесінде жай ғана кірістірілгенін таңдауға мүмкіндік береді.
  • Хаскелл сияқты функциялардың компиляция уақытында тексерілетін түрлері бар. Біздің eDSL-де функцияның кіріс түрі функция күтетін стек түрі болып табылады, ал нәтиже түрі - қоңыраудан кейін жасалатын стек түрі.
  • Кодта аннотациялар бар stacktype, шақыру нүктесінде күтілетін стек түрін сипаттайтын. Әмиян келісім-шартында бұл жай ғана түсініктемелер болды, бірақ біздің eDSL-де олар шын мәнінде кодтың бөлігі болып табылады және компиляция уақытында тексеріледі. Олар код өзгерсе және стек түрі өзгерсе, әзірлеушіге мәселені табуға көмектесетін құжаттама немесе мәлімдеме ретінде қызмет ете алады. Әрине, мұндай аннотациялар орындау уақытының өнімділігіне әсер етпейді, өйткені олар үшін TVM коды жасалмайды.
  • Бұл әлі екі аптада жазылған прототип, сондықтан жобада әлі көп жұмыс істеу керек. Мысалы, төмендегі кодта көрсетілген сыныптардың барлық даналары автоматты түрде жасалуы керек.

Біздің 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

eDSL және көп қолтаңба әмиян келісім-шартымыздың толық бастапқы кодын мына жерден табуға болады бұл репозиторий. Және тағы басқалар егжей-тегжейлі айтып берді кіріктірілген тілдер туралы, біздің әріптесіміз Георгий Агапов.

Байқау және ТОН туралы қорытындылар

Барлығы біздің жұмысымыз 380 сағатқа созылды (соның ішінде құжаттамамен танысу, кездесулер және нақты әзірлеу). Байқау жобасына бес әзірлеуші ​​қатысты: CTO, топ жетекшісі, блокчейн платформасының мамандары және Haskell бағдарламалық жасақтамасын әзірлеушілер.

Біз байқауға еш қиындықсыз қатысу үшін ресурстар таптық, өйткені хакатон рухы, тығыз командалық жұмыс және жаңа технологиялар аспектілеріне тез ену қажеттілігі әрқашан қызықты. Шектеулі ресурстар жағдайында максималды нәтижеге жету үшін бірнеше ұйқысыз түндер баға жетпес тәжірибе мен тамаша естеліктермен өтеледі. Сонымен қатар, мұндай тапсырмаларды орындау әрқашан компанияның процестерінің жақсы сынағы болып табылады, өйткені жақсы жұмыс істейтін ішкі өзара әрекеттесусіз шынымен лайықты нәтижелерге қол жеткізу өте қиын.

Ән мәтінін былай қойғанда: TON командасының атқарған жұмысының көлемі бізді таң қалдырды. Олар күрделі, әдемі, ең бастысы жұмыс істейтін жүйені құра алды. TON өзін үлкен әлеуеті бар платформа ретінде көрсетті. Дегенмен, бұл экожүйені дамыту үшін оны блокчейн жобаларында пайдалану тұрғысынан да, даму құралдарын жетілдіру тұрғысынан да көп нәрсе істеу керек. Біз қазір осы процестің бір бөлігі болғанымызды мақтан тұтамыз.

Егер осы мақаланы оқығаннан кейін сізде әлі де сұрақтарыңыз болса немесе мәселелеріңізді шешу үшін TON пайдалану туралы идеяларыңыз болса, бізге жаз — тәжірибемізбен бөлісуге қуанышты боламыз.

Ақпарат көзі: www.habr.com

пікір қалдыру