FunC pārvērÅ”ana par funkcionālu ar Haskell: kā Serokell uzvarēja Telegram Blockchain konkursā

JÅ«s droÅ”i vien esat dzirdējuÅ”i, ka Telegram gatavojas uzsākt Ton blockchain platformu. Bet jÅ«s, iespējams, palaidāt garām ziņas, kas pirms neilga laika bija Telegram izsludināja konkursu viena vai vairāku viedo lÄ«gumu ievieÅ”anai Å”ai platformai.

Serokell komanda ar lielu pieredzi lielu blokķēdes projektu izstrādē nevarēja stāvēt malā. Mēs konkursam deleģējām piecus darbiniekus, un pēc divām nedēļām viņi tajā ieņēma pirmo vietu ar (ne)pieticÄ«go izlases segvārdu Sexy Chameleon. Å ajā rakstā es runāŔu par to, kā viņi to darÄ«ja. Mēs ceram, ka tuvāko desmit minÅ«Å”u laikā jÅ«s vismaz izlasÄ«siet kādu interesantu stāstu un, maksimums, jÅ«s atradÄ«siet tajā kaut ko noderÄ«gu, ko varēsiet pielietot savā darbā.

Bet sāksim ar nelielu kontekstu.

Sacensības un tās nosacījumi

Tātad dalÄ«bnieku galvenie uzdevumi bija viena vai vairāku piedāvāto viedo lÄ«gumu ievieÅ”ana, kā arÄ« priekÅ”likumu sniegÅ”ana TON ekosistēmas uzlaboÅ”anai. Konkurss norisinājās no 24. septembra lÄ«dz 15. oktobrim, un rezultāti tika paziņoti tikai 15. novembrÄ«. Diezgan ilgu laiku, ņemot vērā, ka Å”ajā laikā Telegram izdevās noturēt un paziņot konkursa rezultātus par lietojumprogrammu projektÄ“Å”anu un izstrādi C++ valodā, lai pārbaudÄ«tu un novērtētu VoIP zvanu kvalitāti Telegram.

Mēs izvēlējāmies divus viedos lÄ«gumus no organizatoru piedāvātā saraksta. Vienam no tiem mēs izmantojām rÄ«kus, kas izplatÄ«ti ar TON, bet otrais tika ieviests jaunā valodā, ko mÅ«su inženieri izstrādāja Ä«paÅ”i TON un iebÅ«vēta Haskell.

Funkcionālās programmÄ“Å”anas valodas izvēle nav nejauÅ”a. MÅ«su korporatÄ«vais emuārs Mēs bieži runājam par to, kāpēc, mÅ«suprāt, funkcionālo valodu sarežģītÄ«ba ir milzÄ«gs pārspÄ«lējums un kāpēc mēs parasti dodam priekÅ”roku tām, nevis objektorientētajām. Starp citu, tas satur arÄ« Ŕī raksta oriÄ£ināls.

Kāpēc mēs vispār nolēmām piedalīties?

ÄŖsāk sakot, tāpēc, ka mÅ«su specializācija ir nestandarta un sarežģīti projekti, kas prasa Ä«paÅ”as prasmes un bieži vien ir zinātniski vērtÄ«gi IT sabiedrÄ«bai. Mēs stingri atbalstām atvērtā pirmkoda attÄ«stÄ«bu un esam iesaistÄ«ti tā popularizÄ“Å”anā, kā arÄ« sadarbojamies ar vadoÅ”ajām Krievijas universitātēm datorzinātņu un matemātikas jomā.

Interesantie konkursa uzdevumi un iesaistÄ«Å”anās mÅ«su iemīļotajā Telegram projektā pati par sevi bija lieliska motivācija, bet balvu fonds kļuva par papildu stimulu. šŸ™‚

TON blokķēdes izpēte

Mēs rÅ«pÄ«gi sekojam jaunākajām tendencēm blokķēdes, mākslÄ«gā intelekta un maŔīnmācÄ«Å”anās jomā un cenÅ”amies nepalaist garām nevienu nozÄ«mÄ«gu izlaidumu katrā jomā, kurā strādājam. Tāpēc lÄ«dz konkursa sākumam mÅ«su komanda jau bija iepazinusies ar idejām no TON balts papÄ«rs. Tomēr pirms darba uzsākÅ”anas ar TON mēs neanalizējām platformas tehnisko dokumentāciju un faktisko pirmkodu, tāpēc pirmais solis bija diezgan acÄ«mredzams - rÅ«pÄ«ga oficiālās dokumentācijas izpēte TieÅ”saistē un projektu krātuves.

LÄ«dz konkursa sākumam kods jau bija publicēts, tāpēc, lai ietaupÄ«tu laiku, nolēmām meklēt ceļvedi vai kopsavilkumu, ko uzrakstÄ«ja lietotāji. Diemžēl tas nedeva nekādus rezultātus ā€“ izņemot instrukcijas platformas montāžai uz Ubuntu, nekādus citus materiālus neatradām.

Pati dokumentācija bija labi izpētÄ«ta, taču dažās jomās to bija grÅ«ti izlasÄ«t. Diezgan bieži nācās atgriezties pie noteiktiem punktiem un pārslēgties no augsta lÄ«meņa abstraktu ideju aprakstiem uz zema lÄ«meņa Ä«stenoÅ”anas detaļām.

BÅ«tu vienkārŔāk, ja specifikācijā vispār nebÅ«tu iekļauts detalizēts ievieÅ”anas apraksts. Informācija par to, kā virtuālā maŔīna attēlo savu kopumu, visticamāk, novirzÄ«s izstrādātāju uzmanÄ«bu, veidojot viedos lÄ«gumus TON platformai, nevis palÄ«dzēs viņiem.

Nix: projekta apvienoŔana

Mēs Serokell esam lieli fani uzmanies. Mēs ar to apkopojam savus projektus un izvietojam tos, izmantojot NixOps, un instalēta visos mūsu serveros Nix OS. Pateicoties tam, visas mūsu versijas ir reproducējamas un darbojas ar jebkuru operētājsistēmu, kurā var instalēt Nix.

Tāpēc mēs sākām ar radÄ«Å”anu Nix pārklājums ar izteiksmi TON montāžai. Ar tās palÄ«dzÄ«bu TON apkopoÅ”ana ir pēc iespējas vienkārŔāka:

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

Ņemiet vērā, ka jums nav jāinstalē nekādas atkarības. Nix maģiski darīs visu jūsu vietā neatkarīgi no tā, vai izmantojat NixOS, Ubuntu vai macOS.

ProgrammēŔana TON

Viedais lÄ«guma kods TON tÄ«klā darbojas TON virtuālajā maŔīnā (TVM). TVM ir sarežģītāka nekā vairums citu virtuālo maŔīnu, un tai ir ļoti interesanta funkcionalitāte, piemēram, ar to var strādāt turpinājumi Šø saites uz datiem.

Turklāt puiÅ”i no TON izveidoja trÄ«s jaunas programmÄ“Å”anas valodas:

Piektā ir universāla steka programmÄ“Å”anas valoda, kas lÄ«dzinās Forth. Viņa superspēja ir spēja mijiedarboties ar TVM.

FunC ir viedā lÄ«guma programmÄ“Å”anas valoda, kas ir lÄ«dzÄ«ga C un ir apkopots citā valodā - Fift Assembler.

Piektais montētājs ā€” Fift bibliotēka bināra izpildāmā koda Ä£enerÄ“Å”anai TVM. Fifth Assembler nav kompilatora. Å is Embedded Domain Specific Language (eDSL).

MÅ«su konkurss darbojas

Visbeidzot, ir pienācis laiks aplūkot mūsu centienu rezultātus.

Asinhronais maksājumu kanāls

Maksājumu kanāls ir vieds lÄ«gums, kas ļauj diviem lietotājiem nosÅ«tÄ«t maksājumus ārpus blokķēdes. Rezultātā jÅ«s ietaupāt ne tikai naudu (nav komisijas maksas), bet arÄ« laiku (jums nav jāgaida nākamā bloka apstrāde). Maksājumi var bÅ«t tik mazi, cik vēlaties, un tik bieži, cik nepiecieÅ”ams. Å ajā gadÄ«jumā pusēm nav jāuzticas viena otrai, jo gala norēķinu godÄ«gumu garantē viedais lÄ«gums.

Mēs atradām diezgan vienkārÅ”u problēmas risinājumu. Divas puses var apmainÄ«ties ar parakstÄ«tiem ziņojumiem, katrā no kuriem ir divi numuri ā€” katras puses maksātā pilna summa. Å ie divi skaitļi darbojas lÄ«dzÄ«gi vektoru pulkstenis tradicionālajās izplatÄ«tajās sistēmās un iestatiet darÄ«jumu secÄ«bu "pirms noticis". Izmantojot Å”os datus, lÄ«gums spēs atrisināt jebkuru iespējamo konfliktu.

PatiesÄ«bā Ŕīs idejas Ä«stenoÅ”anai pietiek ar vienu numuru, bet mēs atstājām abus, jo tādējādi varējām izveidot ērtāku lietotāja interfeisu. Turklāt mēs nolēmām katrā ziņojumā iekļaut maksājuma summu. Bez tā, ja kāda iemesla dēļ ziņa pazÅ«d, tad, lai arÄ« visas summas un gala aprēķins bÅ«s pareizs, lietotājs zaudējumu var nepamanÄ«t.

Lai pārbaudÄ«tu savu ideju, meklējām piemērus Ŕāda vienkārÅ”a un kodolÄ«ga maksājumu kanāla protokola izmantoÅ”anai. PārsteidzoÅ”i, mēs atradām tikai divus:

  1. Apraksts līdzīga pieeja, tikai vienvirziena kanāla gadījumā.
  2. ApmācÄ«ba, kas apraksta to paÅ”u ideju, kas mÅ«su, bet nepaskaidrojot daudzas svarÄ«gas detaļas, piemēram, vispārÄ«gu pareizÄ«bu un konfliktu risināŔanas procedÅ«ras.

Kļuva skaidrs, ka ir jēga detalizēti aprakstÄ«t mÅ«su protokolu, Ä«paÅ”u uzmanÄ«bu pievērÅ”ot tā pareizÄ«bai. Pēc vairākām iterācijām specifikācija bija gatava, un tagad arÄ« jÅ«s varat. Paskaties uz viņu.

Mēs ieviesām lÄ«gumu pakalpojumā FunC un komandrindas utilÄ«tu, lai pilnÄ«bā mijiedarbotos ar mÅ«su lÄ«gumu, Fift, kā to ieteikuÅ”i organizatori. Mēs varējām izvēlēties jebkuru citu valodu savai CLI, taču mēs vēlējāmies izmēģināt Fit, lai redzētu, kā tas darbojas praksē.

GodÄ«gi sakot, pēc darba ar Fift mēs neredzējām nekādus pārliecinoÅ”us iemeslus, lai dotu priekÅ”roku Å”ai valodai populārām un aktÄ«vi lietotām valodām ar izstrādātiem rÄ«kiem un bibliotēkām. ProgrammÄ“Å”ana steka valodā ir diezgan nepatÄ«kama, jo jums pastāvÄ«gi ir jātur galvā tas, kas atrodas stekā, un kompilators ar to nepalÄ«dz.

Tāpēc, mÅ«suprāt, vienÄ«gais Fift pastāvÄ“Å”anas attaisnojums ir tās kā Fift Assembler saimniekvalodas loma. Bet vai nebÅ«tu labāk iegult TVM montētāju kādā esoŔā valodā, nevis izgudrot jaunu Å”im bÅ«tÄ«bā vienÄ«gajam mērÄ·im?

TVM Haskell eDSL

Tagad ir pienācis laiks runāt par mÅ«su otro viedo lÄ«gumu. Mēs nolēmām izstrādāt vairāku parakstu maku, taču rakstÄ«t vēl vienu viedo lÄ«gumu FunC bÅ«tu pārāk garlaicÄ«gi. Mēs vēlējāmies pievienot kādu garÅ”u, un tā bija mÅ«su paÅ”u salikÅ”anas valoda TVM.

Tāpat kā Fift Assembler, mÅ«su jaunā valoda ir iegulta, taču mēs izvēlējāmies Haskell kā saimniekdatoru, nevis Fift, ļaujot mums pilnÄ«bā izmantot tās uzlabotā tipa sistēmu. Strādājot ar viedajiem lÄ«gumiem, kur pat nelielas kļūdas izmaksas var bÅ«t ļoti augstas, statiskā rakstÄ«Å”ana, mÅ«suprāt, ir liela priekÅ”rocÄ«ba.

Lai parādÄ«tu, kā izskatās TVM montētājs, kas ir iegults Haskell, mēs tajā ievietojām standarta maku. Å eit ir dažas lietas, kurām jāpievērÅ” uzmanÄ«ba:

  • Å is lÄ«gums sastāv no vienas funkcijas, taču jÅ«s varat izmantot tik daudz, cik vēlaties. Kad definējat jaunu funkciju resursdatora valodā (t.i., Haskell), mÅ«su eDSL ļauj jums izvēlēties, vai vēlaties, lai tā kļūtu par atseviŔķu TVM rutÄ«nu vai vienkārÅ”i iekļauta zvana punktā.
  • Tāpat kā Haskell, funkcijām ir veidi, kas tiek pārbaudÄ«ti kompilÄ“Å”anas laikā. MÅ«su eDSL funkcijas ievades veids ir steka veids, ko funkcija sagaida, un rezultāta veids ir steka veids, kas tiks izveidots pēc izsaukuma.
  • Kodam ir anotācijas stacktype, aprakstot sagaidāmo steka veidu izsaukuma punktā. Sākotnējā maka lÄ«gumā tie bija tikai komentāri, bet mÅ«su eDSL tie faktiski ir daļa no koda un tiek pārbaudÄ«ti kompilÄ“Å”anas laikā. Tie var kalpot kā dokumentācija vai paziņojumi, kas palÄ«dz izstrādātājam atrast problēmu, ja mainās kods un mainās steka veids. Protams, Ŕādas anotācijas neietekmē izpildlaika veiktspēju, jo tām netiek Ä£enerēts TVM kods.
  • Å is joprojām ir divu nedēļu laikā uzrakstÄ«ts prototips, tāpēc pie projekta vēl ir daudz darāmā. Piemēram, visi tālāk norādÄ«tajā kodā redzamo klaÅ”u gadÄ«jumi ir jāģenerē automātiski.

Šādi izskatās multisig maka ievieŔana mūsu 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

Pilnu mÅ«su eDSL un vairāku parakstu maka lÄ«guma avota kodu var atrast vietnē Å”o repozitoriju. Un vēl sÄ«ki pastāstÄ«ts par iebÅ«vētajām valodām, mÅ«su kolēģis Georgijs Agapovs.

Secinājumi par konkursu un TON

Kopā mÅ«su darbs aizņēma 380 stundas (ieskaitot iepazÄ«Å”anos ar dokumentāciju, sapulces un faktisko izstrādi). Konkursa projektā piedalÄ«jās pieci izstrādātāji: CTO, komandas vadÄ«tājs, blokķēdes platformas speciālisti un Haskell programmatÅ«ras izstrādātāji.

Mēs atradām resursus, lai bez grÅ«tÄ«bām piedalÄ«tos konkursā, jo hakatona gars, cieÅ”s komandas darbs un nepiecieÅ”amÄ«ba ātri iedziļināties jauno tehnoloÄ£iju aspektos vienmēr ir aizraujoÅ”i. Vairākas negulētās naktis, lai sasniegtu maksimālu rezultātu ierobežotu resursu apstākļos, tiek kompensētas ar nenovērtējamu pieredzi un izcilām atmiņām. Turklāt darbs pie Ŕādiem uzdevumiem vienmēr ir labs uzņēmuma procesu tests, jo bez labi funkcionējoÅ”as iekŔējās mijiedarbÄ«bas ir ārkārtÄ«gi grÅ«ti sasniegt patiesi pienācÄ«gus rezultātus.

Dziesmu vārdi malā: mÅ«s pārsteidza TON komandas ieguldÄ«tā darba apjoms. Viņiem izdevās izveidot sarežģītu, skaistu un, pats galvenais, strādājoÅ”u sistēmu. TON ir pierādÄ«jis sevi kā platformu ar lielu potenciālu. Taču, lai Ŕī ekosistēma attÄ«stÄ«tos, ir jādara daudz vairāk gan attiecÄ«bā uz tās izmantoÅ”anu blokķēdes projektos, gan arÄ« attiecÄ«bā uz izstrādes rÄ«ku uzlaboÅ”anu. Mēs esam lepni, ka tagad esam daļa no Ŕī procesa.

Ja pēc Ŕī raksta izlasÄ«Å”anas jums joprojām ir kādi jautājumi vai idejas par to, kā izmantot TON savu problēmu risināŔanai, raksti mums ā€” ar prieku dalÄ«simies pieredzē.

Avots: www.habr.com

Pievieno komentāru