Að breyta FunC í FunCtional með Haskell: Hvernig Serokell vann Telegram Blockchain keppnina

Þú hefur líklega heyrt það Telegram er að fara að koma á markað Ton blockchain vettvang. En þú gætir hafa misst af fréttinni um að ekki er langt síðan Telegram boðaði til samkeppni fyrir innleiðingu á einum eða fleiri snjöllum samningum fyrir þennan vettvang.

Serokell teymið, með mikla reynslu í að þróa stór blockchain verkefni, gat ekki staðið til hliðar. Við sendum fimm starfsmenn í keppnina og tveimur vikum síðar náðu þeir fyrsta sæti í henni undir hinu (ó)hóflega handahófskennda gælunafni Sexy Chameleon. Í þessari grein mun ég tala um hvernig þeir gerðu það. Við vonum að á næstu tíu mínútum lesir þú að minnsta kosti áhugaverða sögu og í mesta lagi finnur þú eitthvað gagnlegt í henni sem þú getur beitt í starfi þínu.

En við skulum byrja á smá samhengi.

Samkeppni og skilyrði hennar

Svo, helstu verkefni þátttakenda voru innleiðing á einum eða fleiri af fyrirhuguðum snjöllum samningum, auk þess að gera tillögur til að bæta TON vistkerfið. Keppnin stóð yfir frá 24. september til 15. október og voru úrslit tilkynnt aðeins 15. nóvember. Nokkuð langur tími, miðað við að á þessum tíma tókst Telegram að halda og tilkynna niðurstöður keppna um hönnun og þróun forrita í C++ til að prófa og meta gæði VoIP-símtala í Telegram.

Við völdum tvo snjalla samninga af listanum sem skipuleggjendur lögðu til. Fyrir annað þeirra notuðum við verkfæri sem dreift var með TON og hið síðara var útfært á nýju tungumáli sem verkfræðingar okkar hafa þróað sérstaklega fyrir TON og innbyggt í Haskell.

Val á virku forritunarmáli er ekki tilviljun. Í okkar fyrirtækjablogg Við tölum oft um hvers vegna okkur finnst flókið hagnýtra tungumála vera miklar ýkjur og hvers vegna við kjósum þau almennt frekar en hlutbundin. Við the vegur, það inniheldur líka frumrit þessarar greinar.

Af hverju ákváðum við að taka þátt?

Í stuttu máli, vegna þess að sérhæfing okkar er óstöðluð og flókin verkefni sem krefjast sérstakrar kunnáttu og hafa oft vísindalegt gildi fyrir upplýsingatæknisamfélagið. Við styðjum eindregið þróun opins hugbúnaðar og tökum þátt í vinsældum þess og erum einnig í samstarfi við leiðandi rússneska háskóla á sviði tölvunarfræði og stærðfræði.

Áhugaverð verkefni keppninnar og þátttaka í okkar ástkæra Telegram verkefni voru í sjálfu sér frábær hvatning, en verðlaunasjóðurinn varð auka hvatning. 🙂

TON blockchain rannsóknir

Við fylgjumst náið með nýrri þróun í blockchain, gervigreind og vélanámi og reynum að missa ekki af einni marktækri útgáfu á hverju sviði sem við vinnum á. Þess vegna, þegar keppnin hófst, var teymið okkar þegar kunnugt um hugmyndir frá TON hvítur pappír. Hins vegar, áður en unnið var með TON, greindum við ekki tækniskjölin og raunverulegan frumkóða vettvangsins, svo fyrsta skrefið var nokkuð augljóst - ítarleg rannsókn á opinberu skjölunum á Online og verkefnageymslur.

Þegar keppnin hófst hafði kóðinn þegar verið birtur, svo til að spara tíma ákváðum við að leita að leiðarvísi eða samantekt skrifuð af notendur. Því miður gaf þetta engar niðurstöður - fyrir utan leiðbeiningar um samsetningu pallsins á Ubuntu fundum við engin önnur efni.

Skjölin sjálf voru vel rannsökuð en stundum var erfitt að lesa þau. Oft þurftum við að fara aftur að ákveðnum atriðum og skipta úr lýsingum á óhlutbundnum hugmyndum á háu stigi yfir í útfærsluupplýsingar á lágu stigi.

Það væri auðveldara ef forskriftin innihélt alls ekki nákvæma lýsingu á útfærslunni. Upplýsingar um hvernig sýndarvél táknar stafla sinn eru líklegri til að afvegaleiða þróunaraðila sem búa til snjalla samninga fyrir TON pallinn en að hjálpa þeim.

Nix: að setja verkefnið saman

Við hjá Serokell erum miklir aðdáendur Nix. Við söfnum verkefnum okkar með því og dreifum þeim með því að nota NixOps, og sett upp á öllum netþjónum okkar Nix OS. Þökk sé þessu er hægt að endurskapa allar smíðin okkar og virka á hvaða stýrikerfi sem er sem hægt er að setja upp Nix á.

Svo við byrjuðum á því að búa til Nix yfirborð með tjáningu fyrir TON samsetningu. Með hjálp hennar er samsetning TON eins einföld og mögulegt er:

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

Athugaðu að þú þarft ekki að setja upp neinar ósjálfstæði. Nix mun gera allt fyrir þig með töfrum, hvort sem þú ert að nota NixOS, Ubuntu eða macOS.

Forritun fyrir TON

Snjall samningskóðinn í TON netinu keyrir á TON sýndarvélinni (TVM). TVM er flóknara en flestar aðrar sýndarvélar og hefur mjög áhugaverða virkni, til dæmis getur það unnið með framhald и tengla á gögn.

Þar að auki bjuggu strákarnir frá TON til þrjú ný forritunarmál:

Fimmtu er alhliða stafla forritunarmál sem líkist Fram. Ofurhæfileiki hans er hæfileikinn til að hafa samskipti við TVM.

FunC er snjallt samningsforritunarmál sem er svipað og C og er sett saman á annað tungumál - Fift Assembler.

Fimmti samsetningarmaður - Fift bókasafn til að búa til tvöfaldan keyranlegan kóða fyrir TVM. Fifth Assembler er ekki með þýðanda. Þetta Embedded Domain Specific Language (eDSL).

Samkeppnin okkar virkar

Að lokum er kominn tími til að skoða árangur af viðleitni okkar.

Ósamstilltur greiðslurás

Greiðslurás er snjall samningur sem gerir tveimur notendum kleift að senda greiðslur utan blockchain. Fyrir vikið spararðu ekki aðeins peninga (það er engin þóknun), heldur líka tíma (þú þarft ekki að bíða eftir að næsta blokk sé afgreidd). Greiðslur geta verið eins litlar og óskað er og eins oft og þörf krefur. Í þessu tilviki þurfa aðilar ekki að treysta hver öðrum, þar sem sanngirni lokauppgjörs er tryggð með snjöllum samningi.

Við fundum frekar einfalda lausn á vandamálinu. Tveir aðilar geta skiptst á undirrituðum skilaboðum, sem hvert um sig inniheldur tvö númer — heildarupphæðin sem hvor aðili greiðir. Þessar tvær tölur virka eins og vektor klukka í hefðbundnum dreifðum kerfum og stilltu "gerist áður" röð á færslur. Með því að nota þessi gögn mun samningurinn geta leyst hugsanleg átök.

Reyndar nægir ein tala til að hrinda þessari hugmynd í framkvæmd, en við hættum bæði vegna þess að þannig gætum við gert þægilegra notendaviðmót. Auk þess ákváðum við að setja greiðsluupphæðina inn í hvert skeyti. Án þess, ef skilaboðin glatast af einhverjum ástæðum, þá gæti notandinn ekki tekið eftir tapinu, þó að allar upphæðir og endanlegur útreikningur sé réttur.

Til að prófa hugmyndina okkar leituðum við að dæmum um að nota svo einfalda og hnitmiðaða greiðslumiðlunarsamskiptareglur. Það kemur á óvart að við fundum aðeins tvö:

  1. Lýsing svipaða nálgun, aðeins ef um er að ræða einstefnurás.
  2. Kennsla, sem lýsir sömu hugmynd og okkar, en án þess að útskýra mörg mikilvæg atriði, svo sem almenna réttmæti og verklagsreglur til að leysa ágreining.

Það varð ljóst að það er skynsamlegt að lýsa samskiptareglum okkar í smáatriðum, með sérstaka athygli á réttmæti hennar. Eftir nokkrar endurtekningar var forskriftin tilbúin og nú getur þú það líka. Líttu á hana.

Við útfærðum samninginn í FunC og við skrifuðum skipanalínutólið til að hafa samskipti við samninginn okkar algjörlega í Fift, eins og skipuleggjendur mæltu með. Við hefðum getað valið hvaða tungumál sem er fyrir CLI okkar, en við höfðum áhuga á að prófa Fit til að sjá hvernig það virkaði í reynd.

Til að vera heiðarlegur, eftir að hafa unnið með Fift, sáum við engar sannfærandi ástæður til að kjósa þetta tungumál en vinsæl og virkt notuð tungumál með þróuðum verkfærum og bókasöfnum. Forritun á stafla-undirstaða tungumáli er frekar óþægilegt, þar sem þú þarft stöðugt að halda í hausnum á þér hvað er á staflanum og þýðandinn hjálpar ekki við þetta.

Þess vegna, að okkar mati, er eina réttlætingin fyrir tilvist Fift hlutverk þess sem gestgjafatungumál fyrir Fift Assembler. En væri ekki betra að fella TVM assembler inn í eitthvert núverandi tungumál, frekar en að finna upp nýtt í þessum eina tilgangi?

TVM Haskell eDSL

Nú er kominn tími til að tala um annan snjalla samninginn okkar. Við ákváðum að þróa veski með mörgum undirskriftum, en að skrifa annan snjallsamning í FunC væri of leiðinlegt. Okkur langaði að bæta við smá bragði og það var okkar eigin samsetningartungumál fyrir TVM.

Eins og Fift Assembler er nýja tungumálið okkar innbyggt, en við völdum Haskell sem gestgjafa í stað Fift, sem gerir okkur kleift að nýta háþróaða tegundakerfið til fulls. Þegar unnið er með snjalla samninga, þar sem kostnaður við jafnvel smá villu getur verið mjög hár, er truflanir á vélritun að okkar mati stór kostur.

Til að sýna hvernig TVM assembler lítur út innbyggt í Haskell, innleiddum við venjulegt veski á það. Hér eru nokkur atriði til að borga eftirtekt til:

  • Þessi samningur samanstendur af einni aðgerð, en þú getur notað eins marga og þú vilt. Þegar þú skilgreinir nýja aðgerð á gestgjafatungumálinu (þ.e. Haskell), gerir eDSL okkar þér kleift að velja hvort þú vilt að það verði aðskilin rútína í TVM eða einfaldlega innbyggð þegar þú hringir.
  • Eins og Haskell, hafa aðgerðir gerðir sem eru athugaðar við þýðingu. Í eDSL okkar er inntaksgerð falls sú tegund stafla sem aðgerðin býst við, og niðurstöðutegundin er sú tegund stafla sem verður framleidd eftir símtalið.
  • Kóðinn er með athugasemdum stacktype, sem lýsir væntanlegri staflagerð við útkallsstöðina. Í upprunalega veskissamningnum voru þetta bara athugasemdir, en í eDSL okkar eru þær í raun hluti af kóðanum og eru athugaðar við samantekt. Þeir geta þjónað sem skjöl eða staðhæfingar sem hjálpa þróunaraðilanum að finna vandamálið ef kóðinn breytist og staflagerðin breytist. Auðvitað hafa slíkar athugasemdir ekki áhrif á frammistöðu keyrslutíma þar sem enginn TVM kóði er búinn til fyrir þær.
  • Þetta er enn frumgerð skrifuð á tveimur vikum, þannig að það er enn mikil vinna fyrir höndum í verkefninu. Til dæmis ættu öll tilvik af flokkunum sem þú sérð í kóðanum hér að neðan að myndast sjálfkrafa.

Svona lítur útfærsla á multisig veski út á eDSL okkar:

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

Hægt er að finna allan frumkóðann eDSL og fjölundirskriftar veskissamningsins á þessari geymslu. Og fleira sagt ítarlega um innbyggð tungumál, samstarfsmaður okkar Georgy Agapov.

Ályktanir um keppnina og TON

Alls tók vinna okkar 380 klukkustundir (þar á meðal kynning á skjölum, fundi og raunveruleg þróun). Fimm verktaki tóku þátt í samkeppnisverkefninu: CTO, teymisstjóri, blockchain vettvangssérfræðingar og Haskell hugbúnaðarhönnuðir.

Við fundum úrræði til að taka þátt í keppninni án erfiðleika, þar sem andi hackathon, náin teymisvinna og þörfin á að sökkva okkur fljótt inn í þætti nýrrar tækni er alltaf spennandi. Nokkrar svefnlausar nætur til að ná hámarksárangri við aðstæður með takmörkuðu fjármagni eru bætt upp með ómetanlegri reynslu og frábærum minningum. Að auki er vinna við slík verkefni alltaf góð prófsteinn á ferla fyrirtækisins, þar sem það er afar erfitt að ná almennilegum árangri án velvirkra innra samskipta.

Til hliðar við texta: við vorum hrifin af þeirri vinnu sem TON teymið lagði á sig. Þeim tókst að byggja upp flókið, fallegt og síðast en ekki síst vinnandi kerfi. TON hefur sannað sig sem vettvangur með mikla möguleika. Hins vegar, til þess að þetta vistkerfi geti þróast, þarf miklu meira að gera, bæði hvað varðar notkun þess í blockchain verkefnum og hvað varðar endurbætur á þróunarverkfærum. Við erum stolt af því að vera hluti af þessu ferli núna.

Ef eftir að hafa lesið þessa grein hefurðu enn einhverjar spurningar eða hefur hugmyndir um hvernig á að nota TON til að leysa vandamál þín, skrifaðu okkur — Við munum vera fús til að deila reynslu okkar.

Heimild: www.habr.com

Bæta við athugasemd