FunC-ը վերածելով ֆունկցիոնալի՝ Haskell-ի հետ. Ինչպես Serokելլը հաղթեց Telegram Blockchain մրցույթում

Դուք հավանաբար լսել եք այդ Telegram-ը պատրաստվում է գործարկել Ton բլոկչեյն հարթակը. Բայց դուք կարող եք բաց թողնել այն լուրերը, որ ոչ վաղ անցյալում Telegram-ը մրցույթ է հայտարարել այս հարթակի համար մեկ կամ մի քանի խելացի պայմանագրերի իրականացման համար:

Serokell թիմը, որն ունի մեծ փորձ բլոկչեյն նախագծերի մշակման գործում, չէր կարող մի կողմ կանգնել: Մենք հինգ աշխատակցի պատվիրակեցինք մրցույթին, և երկու շաբաթ անց նրանք զբաղեցրին առաջին տեղը՝ «սեքսուալ քամելեոն» (ան) պատահական մականունով։ Այս հոդվածում ես կխոսեմ այն ​​մասին, թե ինչպես են նրանք դա արել: Հուսով ենք, որ մոտակա տասը րոպեների ընթացքում դուք գոնե մի հետաքրքիր պատմություն կկարդաք, և առավելագույնը դրա մեջ օգտակար բան կգտնեք, որը կարող եք կիրառել ձեր աշխատանքում։

Բայց եկեք սկսենք մի փոքր համատեքստից:

Մրցույթը և դրա պայմանները

Այսպիսով, մասնակիցների հիմնական խնդիրներն էին առաջարկված խելացի պայմանագրերից մեկի կամ մի քանիսի իրականացումը, ինչպես նաև TON-ի էկոհամակարգի բարելավմանն ուղղված առաջարկների կատարումը։ Մրցույթն անցկացվել է սեպտեմբերի 24-ից հոկտեմբերի 15-ը, իսկ արդյունքները հայտարարվել են միայն նոյեմբերի 15-ին։ Բավականին երկար ժամանակ՝ հաշվի առնելով, որ այս ընթացքում Telegram-ին հաջողվել է անցկացնել և հայտարարել C++ հավելվածների նախագծման և մշակման մրցույթների արդյունքները՝ Telegram-ում VoIP զանգերի որակը փորձարկելու և գնահատելու համար։

Կազմակերպիչների առաջարկած ցանկից ընտրեցինք երկու խելացի պայմանագիր։ Դրանցից մեկի համար մենք օգտագործեցինք TON-ով բաշխված գործիքներ, իսկ երկրորդը ներդրվեց նոր լեզվով, որը մշակվել էր մեր ինժեներների կողմից հատուկ TON-ի համար և ներկառուցված Haskell-ում:

Ֆունկցիոնալ ծրագրավորման լեզվի ընտրությունը պատահական չէ։ Մեր մեջ կորպորատիվ բլոգ Մենք հաճախ ենք խոսում այն ​​մասին, թե ինչու ենք կարծում, որ ֆունկցիոնալ լեզուների բարդությունը հսկայական չափազանցություն է, և ինչու ենք դրանք հիմնականում գերադասում օբյեկտի վրա հիմնված լեզուներից: Ի դեպ, պարունակում է նաև այս հոդվածի բնօրինակը.

Ինչո՞ւ որոշեցինք մասնակցել։

Մի խոսքով, քանի որ մեր մասնագիտացումը ոչ ստանդարտ և բարդ նախագծեր են, որոնք պահանջում են հատուկ հմտություններ և հաճախ գիտական ​​արժեք են ներկայացնում ՏՏ հանրության համար: Մենք մեծապես աջակցում ենք բաց կոդով մշակմանը և զբաղվում ենք դրա հանրահռչակմամբ, ինչպես նաև համագործակցում ենք ռուսական առաջատար համալսարանների հետ համակարգչային գիտության և մաթեմատիկայի ոլորտում:

Մրցույթի հետաքրքիր առաջադրանքները և մեր սիրելի Telegram նախագծում ներգրավվածությունն ինքնին հիանալի մոտիվացիա էին, սակայն մրցանակային ֆոնդը դարձավ լրացուցիչ խթան։ 🙂

TON բլոկչեյնի հետազոտություն

Մենք ուշադիր հետևում ենք բլոկչեյնի, արհեստական ​​ինտելեկտի և մեքենայական ուսուցման նոր զարգացումներին և փորձում բաց չթողնել ոչ մի նշանակալի թողարկում այն ​​ոլորտներից, որտեղ մենք աշխատում ենք: Հետևաբար, մինչ մրցույթը սկսվեց, մեր թիմին արդեն ծանոթ էին գաղափարները TON սպիտակ թուղթ. Այնուամենայնիվ, նախքան TON-ի հետ աշխատանքը սկսելը, մենք չենք վերլուծել տեխնիկական փաստաթղթերը և հարթակի իրական սկզբնական կոդը, ուստի առաջին քայլը բավականին ակնհայտ էր՝ պաշտոնական փաստաթղթերի մանրակրկիտ ուսումնասիրություն: Առցանց իսկ նախագծի շտեմարաններ.

Մինչև մրցույթը սկսվեց, ծածկագիրն արդեն հրապարակված էր, ուստի ժամանակ խնայելու համար մենք որոշեցինք փնտրել ուղեցույց կամ ամփոփագիր, որը գրված էր. օգտագործողների կողմից. Ցավոք, դա ոչ մի արդյունք չտվեց. բացի Ubuntu-ում պլատֆորմը հավաքելու հրահանգներից, մենք այլ նյութեր չգտանք:

Փաստաթղթերն ինքնին լավ ուսումնասիրված էին, բայց որոշ ոլորտներում դժվար էր կարդալ: Հաճախ մենք ստիպված էինք վերադառնալ որոշակի կետերի և վերացական գաղափարների բարձր մակարդակի նկարագրություններից անցնել ցածր մակարդակի իրականացման մանրամասներին:

Ավելի հեշտ կլիներ, եթե հստակեցումը ընդհանրապես չներառեր իրականացման մանրամասն նկարագրությունը: Տեղեկատվությունն այն մասին, թե ինչպես է վիրտուալ մեքենան ներկայացնում իր կույտը, ավելի հավանական է, որ շեղի ծրագրավորողների ուշադրությունը, որոնք խելացի պայմանագրեր են ստեղծում TON հարթակի համար, քան օգնելու նրանց:

Նիքս. նախագիծը միացնելով

Serokell-ում մենք մեծ երկրպագուներ ենք Nix. Դրանով մենք հավաքում ենք մեր նախագծերը և դրանք օգտագործում NixOps, և տեղադրվել մեր բոլոր սերվերների վրա Nix OS. Դրա շնորհիվ մեր բոլոր կառուցվածքները վերարտադրելի են և աշխատում են ցանկացած օպերացիոն համակարգի վրա, որի վրա կարելի է տեղադրել Nix:

Այսպիսով, մենք սկսեցինք ստեղծագործելով Nix ծածկույթը արտահայտությամբ TON հավաքման համար. Նրա օգնությամբ 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 վիրտուալ մեքենայի վրա (TVM): TVM-ն ավելի բարդ է, քան մյուս վիրտուալ մեքենաները, և ունի շատ հետաքրքիր ֆունկցիոնալություն, օրինակ՝ այն կարող է աշխատել շարունակությունները и հղումներ դեպի տվյալներ.

Ավելին, TON-ի տղաները ստեղծեցին ծրագրավորման երեք նոր լեզու.

Հինգ ունիվերսալ stack ծրագրավորման լեզու է, որը նման է Չորրորդը. Նրա սուպեր կարողությունը TVM-ի հետ շփվելու ունակությունն է:

FunC խելացի պայմանագրային ծրագրավորման լեզու է, որը նման է C և կազմվում է մեկ այլ լեզվով՝ Fift Assembler:

Հինգերորդ հավաքող — Հինգ գրադարան TVM-ի համար երկուական գործարկվող կոդ ստեղծելու համար: Fifth Assembler-ը կոմպիլյատոր չունի: Սա Ներկառուցված տիրույթի հատուկ լեզու (eDSL).

Մեր մրցույթն աշխատում է

Վերջապես, ժամանակն է նայելու մեր ջանքերի արդյունքներին:

Ասինխրոն վճարման ալիք

Վճարման ալիքը խելացի պայմանագիր է, որը թույլ է տալիս երկու օգտատերերի վճարումներ ուղարկել բլոկչեյնից դուրս: Արդյունքում դուք խնայում եք ոչ միայն գումար (չկա միջնորդավճար), այլև ժամանակ (պետք չէ սպասել հաջորդ բլոկի մշակմանը): Վճարումները կարող են լինել այնքան փոքր, որքան ցանկանում եք և այնքան հաճախ, որքան պահանջվում է: Այս դեպքում կողմերը չպետք է վստահեն միմյանց, քանի որ վերջնական կարգավորման արդարությունը երաշխավորված է խելացի պայմանագրով։

Մենք գտանք խնդրի բավականին պարզ լուծում. Երկու կողմ կարող են փոխանակել ստորագրված հաղորդագրություններ, որոնցից յուրաքանչյուրը պարունակում է երկու համար՝ յուրաքանչյուր կողմի վճարած ամբողջ գումարը: Այս երկու թվերն աշխատում են այսպես վեկտորային ժամացույց ավանդական բաշխված համակարգերում և սահմանել գործարքների «նախկինում տեղի ունեցած» կարգը: Օգտագործելով այս տվյալները՝ պայմանագիրը կկարողանա լուծել ցանկացած հնարավոր կոնֆլիկտ։

Փաստորեն, մեկ թիվը բավական է այս գաղափարն իրականացնելու համար, բայց մենք երկուսն էլ թողեցինք, քանի որ այս կերպ կարող էինք ավելի հարմար օգտատիրոջ միջերես ստեղծել։ Բացի այդ, մենք որոշեցինք վճարման գումարը ներառել յուրաքանչյուր հաղորդագրության մեջ։ Առանց դրա, եթե հաղորդագրությունը ինչ-ինչ պատճառներով կորել է, ապա, չնայած բոլոր գումարները և վերջնական հաշվարկը ճիշտ կլինեն, օգտատերը կարող է չնկատել կորուստը։

Մեր գաղափարը ստուգելու համար մենք փնտրեցինք վճարման ալիքի նման պարզ և հակիրճ արձանագրության օգտագործման օրինակներ: Զարմանալիորեն մենք գտանք միայն երկուսը.

  1. Նկարագրություն նմանատիպ մոտեցում՝ միայն միակողմանի ալիքի դեպքում։
  2. Ուսուցողական, որը նկարագրում է նույն գաղափարը, ինչ մերը, բայց առանց բացատրելու շատ կարևոր մանրամասներ, ինչպիսիք են ընդհանուր կոռեկտությունը և կոնֆլիկտների լուծման ընթացակարգերը։

Պարզ դարձավ, որ իմաստ ունի մանրամասն նկարագրել մեր արձանագրությունը՝ հատուկ ուշադրություն դարձնելով դրա ճիշտությանը։ Մի քանի կրկնություններից հետո ճշգրտումը պատրաստ էր, և այժմ դուք նույնպես կարող եք: նայիր նրան.

Մենք պայմանագիրն իրականացրեցինք FunC-ում և գրեցինք հրամանի տող օգտակար ծառայությունը մեր պայմանագրի հետ ամբողջությամբ գործակցելու համար Fift-ում, ինչպես խորհուրդ էին տալիս կազմակերպիչները: Մենք կարող էինք ցանկացած այլ լեզու ընտրել մեր CLI-ի համար, բայց մեզ հետաքրքրում էր փորձել Fit-ը՝ տեսնելու, թե ինչպես է այն գործում գործնականում:

Անկեղծ ասած, Fift-ի հետ աշխատելուց հետո մենք չտեսանք որևէ համոզիչ պատճառ այս լեզուն գերադասելու հայտնի և ակտիվ օգտագործվող լեզուներից՝ մշակված գործիքներով և գրադարաններով: Stack-ի վրա հիմնված լեզվով ծրագրավորումը բավականին տհաճ է, քանի որ դուք պետք է անընդհատ ձեր գլխում պահեք այն, ինչ կա ստեկի վրա, և կոմպիլյատորը չի օգնում դրան:

Հետևաբար, մեր կարծիքով, Fift-ի գոյության միակ հիմնավորումը Fift Assembler-ի համար ընդունող լեզվի դերն է։ Բայց ավելի լավ չէ՞ր լինի TVM assembler-ը ներդնել գոյություն ունեցող լեզվի մեջ, այլ ոչ թե նորը հորինել այս էականորեն միակ նպատակով:

TVM Haskell eDSL

Հիմա ժամանակն է խոսել մեր երկրորդ խելացի պայմանագրի մասին: Մենք որոշեցինք մշակել մի քանի ստորագրությամբ դրամապանակ, բայց FunC-ում ևս մեկ խելացի պայմանագիր գրելը չափազանց ձանձրալի կլիներ: Մենք ուզում էինք որոշակի համ ավելացնել, և դա մեր սեփական անսամբլի լեզուն էր TVM-ի համար:

Fift Assembler-ի նման, մեր նոր լեզուն ներդրված է, բայց մենք ընտրեցինք Haskell-ը որպես հյուրընկալող Fift-ի փոխարեն՝ թույլ տալով մեզ լիարժեք օգտվել դրա առաջադեմ տիպային համակարգից: Խելացի պայմանագրերով աշխատելիս, որտեղ նույնիսկ փոքր սխալի արժեքը կարող է շատ բարձր լինել, ստատիկ մուտքագրումը, մեր կարծիքով, մեծ առավելություն է։

Ցույց տալու համար, թե ինչպիսի տեսք ունի TVM assembler-ը, որը ներդրված է Haskell-ում, մենք դրա վրա ներդրեցինք ստանդարտ դրամապանակ: Ահա մի քանի բան, որոնց վրա պետք է ուշադրություն դարձնել.

  • Այս պայմանագիրը բաղկացած է մեկ գործառույթից, բայց դուք կարող եք օգտագործել այնքան, որքան ցանկանում եք: Երբ դուք նոր գործառույթ եք սահմանում հյուրընկալող լեզվով (այսինքն՝ Haskell), մեր eDSL-ը թույլ է տալիս ընտրել՝ արդյոք ցանկանում եք, որ այն դառնա TVM-ում առանձին ռեժիմ, թե պարզապես ներդիրվի զանգի կետում:
  • Haskell-ի նման, ֆունկցիաները ունեն տեսակներ, որոնք ստուգվում են կոմպիլյացիայի ժամանակ։ Մեր eDSL-ում ֆունկցիայի մուտքագրման տեսակը կույտի տեսակն է, որն ակնկալում է ֆունկցիան, իսկ արդյունքի տեսակը կույտի տեսակն է, որը կարտադրվի զանգից հետո:
  • Կոդն ունի ծանոթագրություններ stacktype, նկարագրելով ակնկալվող կույտի տեսակը զանգի կետում: Դրամապանակի սկզբնական պայմանագրում դրանք պարզապես մեկնաբանություններ էին, բայց մեր eDSL-ում դրանք իրականում կոդի մաս են և ստուգվում են կոմպիլյացիայի ժամանակ: Նրանք կարող են ծառայել որպես փաստաթղթեր կամ հայտարարություններ, որոնք օգնում են ծրագրավորողին գտնել խնդիրը, եթե կոդը փոխվի, և փաթեթի տեսակը փոխվի: Իհարկե, նման ծանոթագրությունները չեն ազդում գործարկման ժամանակի աշխատանքի վրա, քանի որ դրանց համար TVM կոդ չի ստեղծվում:
  • Սա դեռ երկու շաբաթվա ընթացքում գրված նախատիպ է, ուստի նախագծի վրա դեռ շատ աշխատանք կա անելու: Օրինակ, դասերի բոլոր օրինակները, որոնք տեսնում եք ստորև կոդում, պետք է ստեղծվեն ավտոմատ կերպով:

Ահա թե ինչ տեսք ունի multisig դրամապանակի ներդրումը մեր 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-ի և բազմաստորագրված դրամապանակի պայմանագրի ամբողջական սկզբնական կոդը կարելի է գտնել այստեղ այս պահոցը: Եւ ավելին մանրամասն պատմեց ներկառուցված լեզուների մասին՝ մեր գործընկեր Գեորգի Ագապովը։

Մրցույթի և TON-ի մասին եզրակացություններ

Ընդհանուր առմամբ, մեր աշխատանքը տևեց 380 ժամ (ներառյալ փաստաթղթերի ծանոթացում, հանդիպումներ և իրական զարգացում): Մրցութային նախագծին մասնակցել են հինգ ծրագրավորողներ՝ CTO, թիմի առաջատար, բլոկչեյն հարթակի մասնագետներ և Haskell ծրագրային ապահովման մշակողներ։

Մրցույթին առանց դժվարության մասնակցելու ռեսուրսներ գտանք, քանի որ հեքըթոնի ոգին, սերտ թիմային աշխատանքը և նոր տեխնոլոգիաների ասպեկտներում արագ խորասուզվելու անհրաժեշտությունը միշտ էլ հուզիչ է: Սահմանափակ ռեսուրսների պայմաններում առավելագույն արդյունքի հասնելու համար մի քանի անքուն գիշերները փոխհատուցվում են անգնահատելի փորձով և հիանալի հիշողություններով: Բացի այդ, նման առաջադրանքների վրա աշխատելը միշտ էլ ընկերության գործընթացների լավ փորձություն է, քանի որ չափազանց դժվար է հասնել իսկապես արժանապատիվ արդյունքների առանց լավ գործող ներքին փոխգործակցության:

Երգերը մի կողմ. մենք տպավորված էինք TON թիմի կատարած աշխատանքի ծավալով: Նրանց հաջողվեց կառուցել բարդ, գեղեցիկ, և ամենակարևորը՝ աշխատող համակարգ։ TON-ն իրեն ապացուցել է որպես մեծ ներուժ ունեցող հարթակ: Այնուամենայնիվ, որպեսզի այս էկոհամակարգը զարգանա, շատ ավելին պետք է արվի՝ ինչպես բլոկչեյն նախագծերում դրա օգտագործման, այնպես էլ զարգացման գործիքների կատարելագործման առումով: Մենք հպարտ ենք, որ այժմ մաս ենք կազմում այս գործընթացին:

Եթե ​​այս հոդվածը կարդալուց հետո դուք դեռ ունեք հարցեր կամ գաղափարներ ունեք, թե ինչպես օգտագործել TON-ը ձեր խնդիրները լուծելու համար, գրեք մեզ — մենք ուրախ կլինենք կիսվել մեր փորձով։

Source: www.habr.com

Добавить комментарий