FörÀndra FunC till FunCtional med Haskell: hur Serokell vann Telegram Blockchain Competition

Du har sÀkert hört talas om att Telegram planerar att lansera blockkedjeplattformen Ton. Men du kanske har missat nyheten att Telegram utlyste en tÀvling för implementering av en eller flera smarta kontrakt för denna plattform.

Teamet pÄ Serokell, med omfattande erfarenhet av utveckling av stora blockkedjeprojekt, kunde inte stÄ vid sidan av. Vi delegerade fem anstÀllda till tÀvlingen, och redan efter tvÄ veckor tog de förstaplatsen med det (inte sÄ) blygsamma slumpmÀssiga namnet Sexy Chameleon. I denna artikel kommer jag att berÀtta om hur de lyckades. Vi hoppas att du inom de nÀrmaste tio minuterna Ätminstone kommer att lÀsa en intressant historia, och som mest hitta nÄgot nyttigt att anvÀnda i ditt arbete.

Men lÄt oss börja med en liten dykning i kontexten.

TĂ€vlingen och dess villkor

SÄ, de huvudsakliga uppgifterna för deltagarna bestod av att implementera ett eller flera av de föreslagna smarta kontrakten, samt att föreslÄ förbÀttringar av TON-ekosystemet. TÀvlingen pÄgick frÄn 24 september till 15 oktober, och resultaten tillkÀnnagavs först den 15 november. Ganska lÀnge, med tanke pÄ att Telegram under denna tid lyckades genomföra och tillkÀnnage resultaten av tÀvlingarna för design och utveckling av applikationer i C++ för att testa och bedöma kvaliteten pÄ VoIP-samtal i Telegram.

Vi valde tvÄ smarta kontrakt frÄn listan som presenterades av arrangörerna. För ett av dem anvÀnde vi verktyg som distribueras tillsammans med TON, och det andra implementerade vi pÄ ett nytt sprÄk som vÄra ingenjörer utvecklade speciellt för TON och integrerade i Haskell.

Valet av funktionellt programmeringssprÄk Àr inte av en slump. I vÄr företagsblogg berÀttar vi ofta varför vi anser att komplexiteten i funktionella sprÄk Àr en stor överdrift och varför vi generellt föredrar dem framför objektorienterade. Förresten, dÀr finns ocksÄ originalet av denna artikel.

Varför bestÀmde vi oss överhuvudtaget för att delta

Kort sagt specialist för oss Àr skrÀddarsydda och komplexa projekt som krÀver speciella fÀrdigheter och ofta har vetenskapligt vÀrde för IT-gemenskapen. Vi stödjer hÀngivet open-source-utveckling och arbetar med att popularisera den, samt samarbetar med ledande universitet i Ryssland inom datavetenskap och matematik.

De intressanta uppgifterna i tĂ€vlingen och vĂ„r koppling till det Ă€lskade projektet Telegram var en stor motivation i sig, och prispotten blev en extra stimulans. 🙂

Forskning om blockchain TON

Vi följer noggrant de nya framstegen inom blockchain, artificiell intelligens och maskininlĂ€rning och försöker inte missa nĂ„gon signifikant release inom varje omrĂ„de dĂ€r vi arbetar. DĂ€rför var vĂ„rt team redan bekant med idĂ©erna frĂ„n TON white paper. Men innan vi började arbeta med TON hade vi inte analyserat den tekniska dokumentationen eller den faktiska kĂ€llkoden för plattformen, sĂ„ det första steget var ganska uppenbart — noggrant studera den officiella dokumentationen pĂ„ webbplats och i projektets repository.

Vid tĂ€vlingens början var koden redan publicerad, sĂ„ för att spara tid beslutade vi att leta efter handledning eller sammanstĂ€llningar skrivna av anvĂ€ndare. TyvĂ€rr gav detta ingen framgĂ„ng — vi hittade inte andra material Ă€n instruktionshandboken för att kompilera plattformen pĂ„ Ubuntu.

SjÀlva dokumentationen visade sig vara noggrant genomarbetad, men det var ibland svÄrt att lÀsa den. Vi fick ofta gÄ tillbaka till olika punkter och vÀxla frÄn hög nivÄ beskrivningar av abstrakta idéer till lÄgnivÄ detaljer av implementationen.

Det skulle ha varit lÀttare om specifikationen överhuvudtaget inte hade en detaljerad beskrivning av implementationen. Informationen om hur den virtuella maskinen representerar sin stack stör snarare utvecklare som skapar smarta kontrakt för TON-plattformen Àn hjÀlper dem.

Nix: vi bygger projektet

PÄ Serokell Àr vi stora fans av Nix. Vi bygger vÄra projekt med den och deployar dem med hjÀlp av NixOps, och pÄ alla vÄra servrar Àr NixOS. Detta gör att alla vÄra byggen Àr reproducerbara och fungerar pÄ vilket operativsystem som helst dÀr Nix kan installeras.

DÀrför började vi med att skapa Nix overlay med ett uttryck för att bygga TON. Med hjÀlp av det Àr det enkelt att kompilera TON:

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

Observera att du inte behöver installera nÄgra beroenden. Nix gör magiskt allt Ät dig oavsett om du anvÀnder NixOS, Ubuntu eller macOS.

Programmering för TON

Koden för smarta kontrakt inom TON Network körs pÄ TON Virtual Machine (TVM). TVM Àr mer komplex Àn de flesta andra virtuella maskiner och har mycket intressanta funktioner, till exempel kan den arbeta med fortsÀttningar (continuations) och datareferenser.

Dessutom har killarna frÄn TON skapat hela tre nya programmeringssprÄk:

Fift — ett mĂ„ngsidigt stackbaserat programmeringssprĂ„k som pĂ„minner om Forth. Dess superkraft Ă€r förmĂ„gan att interagera med TVM.

FunC — ett programmeringssprĂ„k för smarta kontrakt som liknar C och kompileras till ytterligare ett sprĂ„k — Fift Assembler.

Fift Assembler — ett Fift-bibliotek för att generera binĂ€r körbar kod för TVM. Fift Assembler har ingen kompilator. Det Ă€r en inbĂ€ddad domĂ€nspecifik sprĂ„k (eDSL).

VÄra tÀvlingsarbeten

Äntligen Ă€r det dags att titta pĂ„ resultaten av vĂ„ra anstrĂ€ngningar.

Asynkront betalningskanal

Betalningskanalen (payment channel) Àr ett smart kontrakt som lÄter tvÄ anvÀndare göra betalningar utanför blockchain. Detta sparar inte bara pengar (ingen avgift), utan ocksÄ tid (du behöver inte vÀnta pÄ att nÀsta block ska behandlas). Betalningar kan vara hur smÄ som helst och ske sÄ ofta det krÀvs. Parterna behöver inte nödvÀndigtvis lita pÄ varandra, eftersom rÀttvisan i den slutliga avrÀkningen garanteras av smarta kontrakt.

Vi har hittat en ganska enkel lösning pĂ„ problemet. TvĂ„ parter kan byta signerade meddelanden, var och en innehĂ„llande tvĂ„ nummer — det totala belopp som betalats av varje deltagare. Dessa tvĂ„ nummer fungerar som vektorklockor i traditionella distribuerade system och bestĂ€mmer ordningen "skedde före" pĂ„ transaktionerna. Med hjĂ€lp av dessa data kan kontraktet lösa eventuella möjliga konflikter.

För att genomföra denna idé rÀcker det faktiskt med ett enda nummer, men vi har lÀmnat bÄda eftersom det gör att vi kan skapa ett mer anvÀndarvÀnligt grÀnssnitt. Dessutom beslutade vi att inkludera betalningsbeloppet i varje meddelande. Utan detta, om meddelandet av nÄgon anledning gÄr förlorat, kan anvÀndaren missa förlusten, trots att alla belopp och den slutliga berÀkningen Àr korrekta.

För att testa vÄr idé letade vi efter exempel pÄ anvÀndning av ett sÄdant enkelt och koncist betalningskanalsprotokoll. Till vÄr förvÄning upptÀckte vi bara tvÄ:

  1. Beskrivning en liknande metod, men för en envÀgs kanal.
  2. Tutorial, som beskriver samma idé som vÄr, men utan att förklara mÄnga viktiga detaljer, som den övergripande korrektheten och konfliktlösningsproceduren.

Det blev tydligt att det Àr meningsfullt att noggrant beskriva vÄrt protokoll, med sÀrskilt fokus pÄ dess korrekthet. Efter nÄgra iterationer var specificationen klar, och nu kan du ocksÄ titta pÄ den.

Vi implementerade kontraktet pÄ FunC, och verktyget för kommandorad för att interagera med vÄrt kontrakt skrev vi helt pÄ Fift, som rekommenderades av arrangörerna. Vi skulle kunna vÀlja vilket annat sprÄk som helst för vÄr CLI, men vi var intresserade av att prova just Fift för att se hur det presterar i praktiken.

Ärligt talat, efter att ha arbetat med Fift har vi inte sett nĂ„gra övertygande skĂ€l att föredra detta sprĂ„k framför populĂ€ra och aktivt anvĂ€nda sprĂ„k med utvecklade verktyg och bibliotek. Att programmera pĂ„ ett stackbaserat sprĂ„k Ă€r ganska obehagligt, eftersom man hela tiden mĂ„ste hĂ„lla reda pĂ„ vad som ligger i stacken, och kompilatorn hjĂ€lper inte pĂ„ nĂ„got sĂ€tt.

DÀrför anser vi att den enda rimliga anledningen till Fifts existens Àr dess roll som vÀrd-sprÄk för Fift Assembler. Men skulle det inte varit bÀttre att integrera TVM-assemblern i ett befintligt sprÄk istÀllet för att skapa ett nytt för detta i sjÀlva verket enda syfte?

TVM Haskell eDSL

Nu Àr det dags att berÀtta om vÄrt andra smarta kontrakt. Vi beslutade oss för att utveckla en plÄnbok med multisignatur, men att skriva ett ytterligare smart kontrakt pÄ FunC skulle vara alldeles för trÄkigt. Vi ville lÀgga till nÄgot speciellt, och det blev vÄrt eget assembler-sprÄk för TVM.

Precis som Fift Assembler, Àr vÄrt nya inbÀddade sprÄk, men istÀllet för Fift har vi valt Haskell som vÀrd vilket gjorde att vi fullt ut kunde utnyttja dess avancerade typystem. NÀr man arbetar med smarta kontrakt, dÀr kostnaden för Àven ett litet fel kan vara mycket hög, anser vi att statisk typning Àr en stor fördel.

För att demonstrera hur TVM-assemblern inbÀddad i Haskell ser ut, har vi implementerat en standardplÄnbok. HÀr Àr nÄgra saker att beakta:

  • Detta kontrakt bestĂ„r av en funktion, men du kan anvĂ€nda hur mĂ„nga som helst. NĂ€r du definierar en ny funktion pĂ„ vĂ€rd-sprĂ„ket (d.v.s. pĂ„ Haskell), lĂ„ter vĂ„r eDSL dig vĂ€lja om du vill att den ska bli en separat underprogram i TVM eller bara inbĂ€ddas i anropsstĂ€llet.
  • Likt Haskell har funktioner typer som kontrolleras vid kompilering. I vĂ„r eDSL Ă€r typ av funktionens ingĂ„ng den typ av stack som funktionen förvĂ€ntar sig, medan resultattypen Ă€r den typ av stack som kommer att fĂ„s efter anropet.
  • Koden innehĂ„ller annotationer stacktype, som beskriver den förvĂ€ntade typen av stack i anropspunkten. I det ursprungliga plĂ„nboks-kontraktet var dessa bara kommentarer, men i vĂ„r eDSL Ă€r de faktiskt en del av koden och kontrolleras vid kompilering. De kan fungera som dokumentation eller pĂ„stĂ„enden som hjĂ€lper utvecklaren att identifiera problem om typstacken Ă€ndras vid kodĂ€ndringar. SjĂ€lvklart pĂ„verkar sĂ„dana annotationer inte prestandan vid körning, eftersom ingen TVM-kod genereras för dem.
  • Detta Ă€r fortfarande en prototyp som skrevs pĂ„ tvĂ„ veckor, sĂ„ det Ă„terstĂ„r mycket arbete i projektet. Till exempel mĂ„ste alla klassinstanser som du ser i koden nedan genereras automatiskt.

SÄ hÀr ser implementeringen av en multisig-plÄnbok ut i vÄr 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

Den fullstÀndiga kÀllkoden för vÄr eDSL och plÄnbokskontraktet med multisignatur kan du hitta i detta arkiv. En mer detaljerad genomgÄng gav vÄr kollega Georgiy Agapov.

Slutsatser om tÀvlingen och TON

Totalt tog vÄrt arbete 380 timmar (inklusive att sÀtta sig in i dokumentationen, möten och sjÀlva utvecklingen). Fem utvecklare deltog i tÀvlingsprojektet: CTO, teamledare, specialister pÄ blockchain-plattformar och programvaruutvecklare i Haskell.

Vi hittade resurser för att delta i tÀvlingen utan svÄrighet, eftersom hackathon-andan, den nÀra lag-arbetsinsatsen och behovet av att snabbt dyka ner i aspekter av ny teknik alltid Àr fascinerande. NÄgra sömnlösa nÀtter för att uppnÄ maximala resultat under begrÀnsade resurser kompenseras av ovÀrderlig erfarenhet och utmÀrkta minnen. Dessutom Àr arbete med sÄdana uppgifter alltid en bra test pÄ företagets processer, eftersom det Àr extremt svÄrt att uppnÄ verkligt anstÀndiga resultat utan en utmÀrkt inre interaktion.

Förutom lyriken: vi var imponerade av den arbetsvolym som TON-teamet utfört. De lyckades bygga ett komplext, vackert och framför allt fungerande system. TON har visat sig vara en plattform med stor potential. Men för att detta ekosystem ska utvecklas krÀvs det fortfarande mycket arbete, bÄde nÀr det gÀller dess anvÀndning i blockchain-projekt och nÀr det gÀller att förbÀttra utvecklingsverktygen. Vi Àr stolta över att nu vara en del av denna process.

Om du efter att ha lĂ€st den hĂ€r artikeln har nĂ„gra frĂ„gor eller idĂ©er om hur du kan anvĂ€nda TON för att lösa dina uppgifter, hör av dig till oss — vi delar gĂ€rna med oss av vĂ„r erfarenhet.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster