A FunC átalakítása FunCtional-lá Haskell segítségével: hogyan győzött le a Serokell a Telegram Blockchain versenyen

Bizonyára hallott már arról, hogy a Telegram blokklánc platformot indít a Ton néven.De talán lemaradt arról a hírről, hogy nemrégiben a Telegram pályázatot hirdetett a platformhoz kapcsolódó egy vagy több okos szerződés megvalósítására.

A Serokell csapata, amely nagy tapasztalattal rendelkezik a nagy blokklánc projektek fejlesztésében, nem tudott távol maradni. Öt munkatársunkat delegáltuk a pályázatra, és már két héttel később az első helyet szerezték meg egy (nem) szerény véletlenszerű néven, Sexy Chameleon. Ebben a cikkben arról fogok mesélni, hogy hogyan sikerült ezt elérniük. Reméljük, hogy a következő tíz percben legalább egy érdekes történetet olvas el, de lehet, hogy talál benne valami hasznosat, amit a munkájában alkalmazhat.

De kezdjük egy kis kontextus áttekintésével.

A pályázat és annak feltételei

Tehát a résztvevők fő feladatai az voltak, hogy megvalósítsanak egy vagy több javasolt okos szerződést, valamint javaslatokat tegyenek a TON ökoszisztéma fejlesztésére. A pályázat szeptember 24-től október 15-ig tartott, az eredményeket pedig csak november 15-én hirdették ki. Ez viszonylag hosszú idő, figyelembe véve, hogy ez idő alatt a Telegram már lebonyolította és kihirdette a dizájn- és C++ alkalmazásfejlesztési verseny eredményeit, melyek célja a VoIP-hívások tesztelése és minőségének értékelése volt a Telegramban.

Két okos szerződést választottunk a szervezők által javasolt listából. Az egyikhez a TON-nal együtt terjesztett eszközöket használtuk, a másikat pedig egy új nyelven valósítottuk meg, amelyet mérnökeink kifejezetten a TON számára fejlesztettek ki, és amelyet Haskellbe integráltak.

A funkcionális programozási nyelv választása nem véletlen. A mi vállalati blogunkon gyakran beszélünk arról, hogy miért tartjuk a funkcionális nyelvek bonyolultságát nagymértékű túlzásnak, és hogy miért részesítjük előnyben azokat az objektumorientáltakkal szemben. Egyébként ott található a cikk eredeti változata..

Miért döntöttünk a részvétel mellett

Röviden azért, mert szakterületünk a nem hagyományos és bonyolult projektek, amelyek különleges készségeket igényelnek és sok esetben tudományos értéket képviselnek az IT közösség számára. Melegen támogatjuk az open-source fejlesztést és népszerűsítjük azt, valamint együttműködünk Oroszország vezető egyetemeivel a számítástechnika és a matematika területén.

A versenyi feladatok és a szívesen támogatott Telegram-projektünkhöz való hozzájárulás már önmagában is nagyszerű motivációt jelentett, a nyereményalap pedig további ösztönzést adott. 🙂

A TON blokklánc kutatása

Gondosan figyelemmel kísérjük a blokklánccal, mesterséges intelligenciával és gépi tanulással kapcsolatos új fejlesztéseket, és igyekszünk nem elmulasztani egyetlen jelentős kiadást sem az általunk kezelt területeken. Ezért a verseny kezdetére csapatunk már ismerte az ötleteket a TON white paper-ből.Viszont a TON-nal való munka megkezdése előtt nem elemeztük a technikai dokumentációt és a platform tényleges forráskódját, így az első lépés egyértelmű volt - alapos tanulmányozás az hivatalos dokumentációban a weboldalon és a projekt tárolójában.

A verseny kezdetére a kód már közzétételre került, így az időmegtakarítás érdekében úgy döntöttünk, hogy keresünk egy útmutatót vagy összefoglalót, amelyet felhasználók. Sajnos ez nem hozott eredményt - az Ubuntu platformra való telepítés útmutatóján kívül nem találtunk más anyagokat.

A dokumentáció alaposan kidolgozott volt, de néhány részletet nehéz volt olvasni. Gyakran kellett visszatérnünk bizonyos pontokhoz, és a magas szintű absztrakt leírásokból a részletes megvalósítási részletekre váltani.

Egyszerűbb lett volna, ha a specifikációban egyáltalán nincsenek részletes végrehajtási leírások. Az információ arról, hogy a virtuális gép hogyan képviseli a saját veremét, inkább eltereli a fejlesztők figyelmét, akik okos szerződéseket készítenek a TON platformra, mintsem hogy segítsenek nekik.

Nix: projekt összegyűjtése

A Serokell-nél nagy rajongói vagyunk Nix. Mi ezeket a projekteket NixOps segítségével állítjuk elő és telepítjük . Minden szerverünkön telepítve van. Ennek köszönhetően minden build reprodukálható és bármilyen operációs rendszeren működik, amelyre Nix telepíthető. NixOSEzért elkezdtük létrehozni a

Nix overlay-t a TON összegyűjtésével kapcsolatos kifejezéssel. Ezzel a TON kompilálása maximálisan egyszerű:$ cd ~/.config/nixpkgs/overlays && git clone https://github.com/serokell/ton.nix $ cd /path/to/ton/repo && nix-shell [nix-shell]$ cmakeConfigurePhase && make

Fontos megjegyezni, hogy nem szükséges semmilyen függőséget telepíteni. A Nix varázslatos módon mindent megold helyetted, függetlenül attól, hogy NixOS-t, Ubuntut vagy macOS-t használsz.

Programozás a TON számára.

Programozás a TON számára

A TON Network okos szerződéseinek kódja a TON Virtuális Gépen (TVM) fut. A TVM bonyolultabb, mint a legtöbb más virtuális gép, és nagyon érdekes funkciókkal rendelkezik, például képes kezelni folytonosságokat (continuations) és adatlinkeket.

Továbbá, a TON csapata három új programozási nyelvet alkotott:

Fift — egy univerzális veremalapú programozási nyelvet, amely hasonlít a ForthNyújtja a TVM-mel való kölcsönhatás lehetőségét.

FunC — egy okos szerződések programozási nyelvét, amely hasonlít a C és egy másik nyelvre fordul, a Fift Assemblerré.

Fift Assembler — a Fift könyvtár a TVM-hez bináris végrehajtható kód generálásához. A Fift Assembler nem rendelkezik fordítóval. Ez egy beágyazott tárgy-orientált nyelv (eDSL).

A versenyműveink

Végre elérkezett az idő, hogy megvizsgáljuk erőfeszítéseink eredményeit.

Aszinkron fizetési csatorna

A fizetési csatorna (payment channel) egy okos szerződés, amely lehetővé teszi, hogy két felhasználó kifizetéseket küldjenek a blokkláncon kívül. Ennek következtében nemcsak pénzt takarítanak meg (nincs díj), hanem időt is (nem kell várni, amíg feldolgozásra kerül a következő blokk). A kifizetések bármilyen kicsiek lehetnek, és annyiszor történhetnek, ahányszor szükséges. A feleknek nem szükséges megbízniuk egymásban, mert a végső elszámolás igazságosságát az okos szerződés garantálja.

Találtunk egy meglehetősen egyszerű megoldást a problémára. A két fél aláírt üzeneteket cserélhet, amelyek mindegyike két számot tartalmaz — az egyes résztvevők által kifizetett teljes összeget. Ezek a számok működnek, mint vektoros órák a hagyományos elosztott rendszerekben, és meghatározzák a tranzakciók közötti "történik előbb" sorrendet. Ezeknek az adatoknak a felhasználásával a szerződés képes lesz megoldani bármilyen lehetséges konfliktust.

Valójában ennek az ötletnek a megvalósításához elegendő egy szám, de mindkettőt meghagytuk, mert így egy kényelmesebb felhasználói felületet tudtunk létrehozni. Ezen kívül minden üzenetbe szeretnénk belefoglalni a kifizetés összegét. Enélkül, ha az üzenet valamilyen okból elveszik, akkor, bár az összes összeg és a végső elszámolás helyes lesz, a felhasználó esetleg nem veszi észre az elveszítést.

Az ötletünk ellenőrzésére keresni kezdtünk példákat egy ilyen egyszerű és tömör fizetési csatorna alkalmazására. Meglepetésünkre mindössze kettőt találtunk:

  1. Leírás hasonló megközelítést, csak az egyirányú csatorna esetére.
  2. Útmutató, amely ugyanazt az elképzelést tartalmazza, mint a miénk, csupán a fontos részletek, mint például a teljes megbízhatóság és a konfliktusok megoldásának eljárása, magyarázat nélkül.

Nyilvánvalóvá vált, hogy érdemes részletesen leírni a protokollunkat, különös figyelmet fordítva a megbízhatóságára. Néhány iteráció után a specifikáció elkészült, és most már ti is megnézhetitek.

A szerződést FunC nyelven valósítottuk meg, a szerződésünkkel való interakcióra szolgáló parancssori eszközt pedig teljes egészében Fift nyelven írtuk, ahogyan a szervezők javasolták. Bármely más nyelvet választhattunk volna a CLI-hez, de minket kifejezetten érdekelt, hogy a Fift-tel dolgozzunk, hogy lássuk, milyen teljesítményt nyújt.

Őszintén szólva, miután dolgoztunk a Fift-tel, nem találtunk meggyőző okokat arra, hogy ezt a nyelvet előnyben részesítsük a népszerű és széles körben használt nyelvekkel szemben, amelyek fejlett eszközkészlettel és könyvtárakkal rendelkeznek. Programozni egy verem-alapú nyelven meglehetősen kellemetlen, mert folyamatosan észben kell tartani, hogy éppen mi hol helyezkedik el a veremben, és ezzel a fordító nem segít.

Ezért a Fift létezésének egyetlen, véleményünk szerint indokolt szerepe a Fift Assembler számára kijelölt host-nyelv. De nem lett volna célszerűbb a TVM assemblerét beépíteni egy meglévő nyelvbe, ahelyett, hogy egy újat találnánk ki tisztán ennek az egyetlen célnak?

TVM Haskell eDSL

Most elérkezett az idő, hogy beszámoljunk a második intelligens szerződésünkről. Úgy döntöttünk, hogy egy több aláírásos tárcát fejlesztünk, de egy újabb okos szerződést FunC nyelven írni túl unalmas lenne. Szerettünk volna valami különlegeset hozzáadni, így most a saját assembler nyelvünket alkottuk meg a TVM számára.

A Fift Assemblerhez hasonlóan új nyelvünk is beépíthető, de a Fift helyett a Haskellt választottuk hostnak, ami lehetővé tette számunkra, hogy teljes mértékben kihasználjuk annak fejlett típuskezelési rendszerét. Okos szerződésekkel való munka során, ahol egy apró hiba ára rendkívül magas lehet, a statikus típusosság szerintünk nagy előny.

Annak érdekében, hogy bemutassuk, hogyan néz ki a TVM assembler a Haskell-ben, megvalósítottunk egy standard tárcát. Itt van néhány dolog, amire érdemes figyelni:

  • Ez a szerződés egy funkcióból áll, de annyit használhatsz, amennyit csak akarsz. Amikor új funkciót definiálsz a host nyelvén (vagyis Haskell-ben), az eDSL-ünk lehetővé teszi, hogy eldöntsd, szeretnéd-e, hogy az külön programként alakuljon át a TVM-ben, vagy csak beépüljön a hívási helyre.
  • A Haskell-hez hasonlóan a funkcióknak vannak típusaik, amelyeket a fordítás során ellenőriznek. Az eDSL-ünkben a funkció bemeneti típusa az a stack típus, amelyet a funkció vár, míg az eredménnyel a stack típusát jelöljük, amely a hívás után keletkezik.
  • A kódban annotációk találhatóak stacktype, amelyek leírják a várt stack típust a hívási ponton. Az eredeti tárca szerződésében ezek csak megjegyzések voltak, de az eDSL-ünkben valójában a kód részét képezik és a fordítás során ellenőrzik őket. Ezek dokumentációként vagy állításként szolgálhatnak, amelyek segítenek a fejlesztőnek megtalálni a problémát, ha a kód módosításakor a stack típusa megváltozik. Természetesen az ilyen annotációk nem befolyásolják a futási teljesítményt, mivel ezekhez nem generálódik TVM kód.
  • Ez még mindig egy prototípus, amelyet két hét alatt írtunk, ezért a projekten még sok munka vár ránk. Például az összes, az alábbi kódban látható osztálypéldányt automatikusan kell generálni.

Így néz ki a multisig tárca megvalósítása az eDSL-ünkben:

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

A teljes forráskódunkat az eDSL-ről és a multisign wallet szerződésről megtalálja ebben a repositoryban. Egy részletesebb beszámolót tett a beépített nyelvekről kollégánk, Georgij Agapov tett közzé.

A verseny végső következtetései és a TON

Összesen 380 órát töltöttünk a projekten (a dokumentáció megismerésével, megbeszélésekkel és a tényleges fejlesztéssel együtt). A versenyprojektben öt fejlesztő vett részt: CTO, csapatvezető, blokklánc-platform szakértők és Haskell programozók.

A versenysorozat részvételéhez szükséges erőforrásokat könnyedén megtaláltuk, hiszen a hackathon szelleme, a szoros csapatmunka, és az új technológiák gyors megismerésének szükségessége mindig izgalmas. Néhány álmatlan éjszaka a maximális eredmény elérése érdekében korlátozott erőforrások mellett felbecsülhetetlen tapasztalatokkal és remek emlékekkel jár. Ráadásul az ilyen feladatokkal való munka mindig jó teszt a vállalat folyamatai számára, mivel igazán értékes eredményeket elérni anélkül, hogy a belső együttműködés tökéletesen működjön, rendkívül nehéz.

A lirika mellett: lenyűgözött minket a TON csapat által végzett munka mennyisége. Képesek voltak felépíteni egy bonyolult, szép, és ami a legfontosabb, működő rendszert. A TON olyan platformként mutatkozott be, amely nagy potenciállal rendelkezik. Azonban ahhoz, hogy ez az ökoszisztéma fejlődjön, még rengeteg tennivaló van, mind a blokklánc projektekben való felhasználás, mind a fejlesztési eszközök tökéletesítése szempontjából. Büszkék vagyunk arra, hogy mostantól részei vagyunk ennek a folyamatnak.

Ha a cikk elolvasása után kérdéseid maradtak, vagy ötleteid vannak arról, hogyan alkalmazhatod a TON-t a feladataid megoldására, írj nekünk — örömmel megosztjuk tapasztalatainkat.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster