Transformi FunC en FunCtional kun Haskell: Kiel Serokell gajnis la Telegram-Blokan Konkurson

Vi verŝajne aŭdis tiun Telegramon estas lanĉota la platformo de blokĉeno Ton. Sed vi eble maltrafis la novaĵon, ke antaŭ nelonge Telegramo anoncis konkurson por la efektivigo de unu aŭ pluraj inteligentaj kontraktoj por ĉi tiu platformo.

La Serokell-teamo, kun ampleksa sperto pri evoluigado de grandaj blokĉenaj projektoj, ne povis flankenmeti. Ni delegis kvin dungitojn al la konkurso, kaj du semajnojn poste ili prenis la unuan lokon en ĝi sub la (ne)modesta hazarda kromnomo Sexy Chameleon. En ĉi tiu artikolo mi parolos pri kiel ili faris ĝin. Ni esperas, ke en la venontaj dek minutoj vi almenaŭ legos interesan rakonton, kaj maksimume vi trovos en ĝi ion utilan, kiun vi povas apliki en via laboro.

Sed ni komencu per iom da kunteksto.

Konkurado kaj ĝiaj kondiĉoj

Do, la ĉefaj taskoj de la partoprenantoj estis la efektivigo de unu aŭ pli el la proponitaj inteligentaj kontraktoj, kaj ankaŭ fari proponojn por plibonigi la TON-ekosistemon. La konkurso kuris de la 24-a de septembro ĝis la 15-a de oktobro, kaj la rezultoj estis anoncitaj nur la 15-an de novembro. Sufiĉe longa tempo, konsiderante, ke dum ĉi tiu tempo Telegramo sukcesis okazigi kaj anonci la rezultojn de konkursoj pri la dezajno kaj disvolviĝo de aplikoj en C++ por testi kaj taksi la kvaliton de VoIP-vokoj en Telegram.

Ni elektis du inteligentajn kontraktojn el la listo proponita de la organizantoj. Por unu el ili, ni uzis ilojn distribuitajn per TON, kaj la dua estis efektivigita en nova lingvo evoluigita de niaj inĝenieroj specife por TON kaj konstruita en Haskell.

La elekto de funkcia programlingvo ne estas hazarda. En nia kompania blogo Ni ofte parolas pri kial ni pensas, ke la komplekseco de funkciaj lingvoj estas grandega troigo kaj kial ni ĝenerale preferas ilin al objekto-orientitaj. Cetere, ĝi ankaŭ enhavas originalo de ĉi tiu artikolo.

Kial ni eĉ decidis partopreni?

Mallonge, ĉar nia specialiĝo estas ne-normaj kaj kompleksaj projektoj, kiuj postulas specialajn kapablojn kaj ofte havas sciencan valoron por la IT-komunumo. Ni forte subtenas malfermfontan disvolviĝon kaj okupiĝas pri ĝia popularigo, kaj ankaŭ kunlaboras kun ĉefaj rusaj universitatoj en la kampo de komputiko kaj matematiko.

La interesaj taskoj de la konkurso kaj engaĝiĝo en nia amata Telegram-projekto estis en si mem bonega instigo, sed la premiofonduso fariĝis plia instigo. 🙂

Esploro pri blokĉeno de TON

Ni atente kontrolas novajn evoluojn en blokĉeno, artefarita inteligenteco kaj maŝinlernado kaj provas ne maltrafi eĉ unu signifan eldonon en ĉiu el la areoj en kiuj ni laboras. Tial, kiam la konkurso komenciĝis, nia teamo jam konis ideojn de TON blanka papero. Tamen antaŭ ol komenci labori kun TON, ni ne analizis la teknikan dokumentadon kaj la realan fontkodon de la platformo, do la unua paŝo estis sufiĉe evidenta - ĝisfunda studo de la oficiala dokumentado pri ejo kaj en projektaj deponejoj.

Kiam la konkurso komenciĝis, la kodo jam estis publikigita, do por ŝpari tempon, ni decidis serĉi gvidilon aŭ resumon skribitan de uzantoj. Bedaŭrinde, tio ne donis rezultojn - krom instrukcioj por kunmeti la platformon sur Ubuntu, ni ne trovis aliajn materialojn.

La dokumentaro mem estis bone esplorita, sed estis malfacile legebla en kelkaj lokoj. Sufiĉe ofte ni devis reveni al certaj punktoj kaj ŝanĝi de altnivelaj priskriboj de abstraktaj ideoj al malaltnivelaj realigaj detaloj.

Estus pli facile se la specifo tute ne inkludus detalan priskribon de la efektivigo. Informoj pri kiel virtuala maŝino reprezentas sian stakon pli verŝajne distri programistojn kreantajn inteligentajn kontraktojn por la TON-platformo ol helpi ilin.

Nix: kunmetante la projekton

Ĉe Serokell ni estas grandaj adorantoj nix. Ni kolektas niajn projektojn kun ĝi kaj disfaldi ilin uzante NixOps, kaj instalita sur ĉiuj niaj serviloj Nix OS. Dank'al ĉi tio, ĉiuj niaj konstruaĵoj estas reprodukteblaj kaj funkcias en ajna operaciumo sur kiu Nix povas esti instalita.

Do ni komencis per kreado Nix-kovraĵo kun esprimo por TON-asembleo. Kun ĝia helpo, kompili TON estas kiel eble plej simpla:

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

Notu, ke vi ne bezonas instali dependecojn. Nix magie faros ĉion por vi, ĉu vi uzas NixOS, Ubuntu aŭ macOS.

Programado por TON

La inteligenta kontraktokodo en la TON-Reto funkcias per la TON Virtuala Maŝino (TVM). TVM estas pli kompleksa ol la plej multaj aliaj virtualaj maŝinoj, kaj havas tre interesan funkciecon, ekzemple, ĝi povas funkcii kun daŭrigoj и ligiloj al datumoj.

Krome, la uloj de TON kreis tri novajn programlingvojn:

Kvin estas universala staka programlingvo kiu similas antaŭen. Lia bonega kapablo estas la kapablo interagi kun TVM.

FunC estas inteligenta kontrakta programlingvo, kiu similas al C kaj estas kompilita en alian lingvon - Fift Assembler.

Kvina Asemblanto — Kvin biblioteko por generi binaran plenumeblan kodon por TVM. Fifth Assembler ne havas kompililon. Ĉi tio Enigita Domajna Specifa Lingvo (eDSL).

Nia konkurso funkcias

Fine, estas tempo rigardi la rezultojn de niaj klopodoj.

Nesinkrona pagokanalo

Pagkanalo estas inteligenta kontrakto, kiu permesas al du uzantoj sendi pagojn ekster la blokĉeno. Kiel rezulto, vi ŝparas ne nur monon (ne estas komisiono), sed ankaŭ tempon (vi ne devas atendi ke la sekva bloko estos procesita). Pagoj povas esti tiel malgrandaj kiel dezirate kaj tiel ofte kiel necese. En ĉi tiu kazo, la partioj ne devas fidi unu la alian, ĉar la justeco de la fina kompromiso estas garantiita de la inteligenta kontrakto.

Ni trovis sufiĉe simplan solvon al la problemo. Du partioj povas interŝanĝi subskribitajn mesaĝojn, ĉiu enhavante du nombrojn—la plenan kvanton pagita de ĉiu partio. Ĉi tiuj du nombroj funkcias kiel vektora horloĝo en tradiciaj distribuitaj sistemoj kaj starigis la "okazis antaŭe" ordon pri transakcioj. Uzante ĉi tiujn datumojn, la kontrakto povos solvi ajnan eblan konflikton.

Fakte, unu nombro sufiĉas por efektivigi ĉi tiun ideon, sed ni lasis ambaŭ ĉar tiel ni povus fari pli oportunan uzantinterfacon. Krome, ni decidis enmeti la pagsumon en ĉiu mesaĝo. Sen ĝi, se la mesaĝo estas perdita ial, tiam, kvankam ĉiuj sumoj kaj la fina kalkulo estos ĝustaj, la uzanto eble ne rimarkos la perdon.

Por testi nian ideon, ni serĉis ekzemplojn de uzado de tia simpla kaj konciza pagkanala protokolo. Surprize, ni trovis nur du:

  1. Priskribo simila aliro, nur por la kazo de unudirekta kanalo.
  2. Lernilo, kiu priskribas la saman ideon kiel la nia, sed sen klarigi multajn gravajn detalojn, kiel ĝeneralan ĝustecon kaj konfliktsolvajn procedurojn.

Evidentiĝis, ke estas senco priskribi nian protokolon detale, atentante ĝian ĝustecon. Post pluraj ripetoj, la specifo estis preta, kaj nun vi ankaŭ povas. rigardu ŝin.

Ni efektivigis la kontrakton en FunC, kaj ni skribis la komandlinian ilon por interagi kun nia kontrakto tute en Fift, kiel rekomendite de la organizantoj. Ni povintus elekti ajnan alian lingvon por nia CLI, sed ni interesiĝis provi Fit por vidi kiel ĝi rezultas praktike.

Verdire, post laborado kun Fift, ni ne vidis iujn devigajn kialojn preferi ĉi tiun lingvon al popularaj kaj aktive uzataj lingvoj kun evoluintaj iloj kaj bibliotekoj. Programi en stak-bazita lingvo estas sufiĉe malagrabla, ĉar vi devas konstante konservi en via kapo tion, kio estas sur la stako, kaj la kompililo ne helpas pri tio.

Tial, laŭ nia opinio, la sola pravigo por la ekzisto de Fift estas ĝia rolo kiel gastiga lingvo por Fift Assembler. Sed ĉu ne estus pli bone enigi la TVM-asemblen en iun ekzistantan lingvon, ol inventi novan por ĉi tiu esence sola celo?

TVM Haskell eDSL

Nun estas tempo paroli pri nia dua inteligenta kontrakto. Ni decidis evoluigi multsignatan monujon, sed skribi alian inteligentan kontrakton en FunC estus tro enuiga. Ni volis aldoni iom da gusto, kaj tio estis nia propra asembla lingvo por TVM.

Kiel Fift Assembler, nia nova lingvo estas enigita, sed ni elektis Haskell kiel la gastiganton anstataŭ Fift, permesante al ni plene profiti ĝian altnivelan tipsistemon. Kiam vi laboras kun inteligentaj kontraktoj, kie la kosto de eĉ eta eraro povas esti tre alta, statika tajpado, laŭ nia opinio, estas granda avantaĝo.

Por pruvi kiel aspektas TVM-asemblero enigita en Haskell, ni efektivigis norman monujon sur ĝi. Jen kelkaj aferoj por atenti:

  • Ĉi tiu kontrakto konsistas el unu funkcio, sed vi povas uzi tiom da kiom vi volas. Kiam vi difinas novan funkcion en la gastiga lingvo (t.e. Haskell), nia eDSL permesas al vi elekti ĉu vi volas, ke ĝi fariĝu aparta rutino en TVM aŭ simple enliniita ĉe la alvoko.
  • Kiel Haskell, funkcioj havas tipojn kiuj estas kontrolitaj ĉe kompiltempo. En nia eDSL, la eniga tipo de funkcio estas la speco de stako, kiun la funkcio atendas, kaj la rezulta tipo estas la speco de stako kiu estos produktita post la voko.
  • La kodo havas komentadojn stacktype, priskribante la atendatan stakspecon ĉe la vokpunkto. En la originala monujo-kontrakto ĉi tiuj estis nur komentoj, sed en nia eDSL ili efektive estas parto de la kodo kaj estas kontrolitaj je kompilo. Ili povas servi kiel dokumentado aŭ deklaroj, kiuj helpas la programiston trovi la problemon se la kodo ŝanĝiĝas kaj la stako tipo ŝanĝiĝas. Kompreneble, tiaj komentarioj ne influas rultempan rendimenton, ĉar neniu TVM-kodo estas generita por ili.
  • Ĉi tio ankoraŭ estas prototipo skribita en du semajnoj, do ankoraŭ estas multe da laboro farenda pri la projekto. Ekzemple, ĉiuj okazoj de la klasoj, kiujn vi vidas en la suba kodo, devus esti generitaj aŭtomate.

Jen kiel aspektas la efektivigo de multisig-monujo en nia 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

La plena fontkodo de nia eDSL kaj plursubskriba monujo-kontrakto troveblas ĉe ĉi tiu deponejo. Kaj pli rakontis detale pri enkonstruitaj lingvoj, nia samideano Georgy Agapov.

Konkludoj pri la konkurado kaj TON

Entute, nia laboro daŭris 380 horojn (inkluzive de konatiĝo kun dokumentado, renkontiĝoj kaj efektiva evoluo). Kvin programistoj partoprenis en la konkuradprojekto: CTO, teamgvidanto, blockchain platformo specialistoj kaj Haskell programaro programistoj.

Ni trovis rimedojn por partopreni la konkurson sen malfacileco, ĉar la spirito de hakatono, proksima teamlaboro kaj la bezono rapide mergi nin en aspektoj de novaj teknologioj estas ĉiam ekscita. Pluraj sendormaj noktoj por atingi maksimumajn rezultojn en kondiĉoj de limigitaj rimedoj estas kompensitaj per valorega sperto kaj bonegaj memoroj. Krome, labori pri tiaj taskoj ĉiam estas bona provo de la procezoj de la kompanio, ĉar estas ege malfacile atingi vere decajn rezultojn sen bone funkcianta interna interago.

Kantotekstoj flankenmetite: ni estis impresitaj de la kvanto da laboro farita de la TON-teamo. Ili sukcesis konstrui kompleksan, belan, kaj plej grave, funkciantan sistemon. TON montris sin kiel platformo kun granda potencialo. Tamen, por ke ĉi tiu ekosistemo evoluu, multe pli devas esti farita, kaj laŭ ĝia uzo en blokĉenaj projektoj kaj laŭ plibonigo de disvolvaj iloj. Ni fieras nun esti parto de ĉi tiu procezo.

Se post legi ĉi tiun artikolon vi ankoraŭ havas demandojn aŭ havas ideojn pri kiel uzi TON por solvi viajn problemojn, skribu al ni — ni volonte kundividos nian sperton.

fonto: www.habr.com

Aldoni komenton