A FunC funkcionalitása Haskellel: Hogyan nyerte meg Serokell a Telegram Blockchain versenyt

Valószínűleg hallottad már ezt a Telegramot hamarosan elindítja a Ton blokklánc platformot. De lehet, hogy lemaradt a hírről, amit nem sokkal ezelőtt a Telegram versenyt hirdetett egy vagy több intelligens szerződés megvalósításához ehhez a platformhoz.

A nagy blokklánc-projektek fejlesztésében nagy tapasztalattal rendelkező Serokell csapata nem állhatott félre. Öt alkalmazottat delegáltunk a megmérettetésre, és két héttel később ők szerezték meg az első helyezést a (szerénytelen) véletlenszerű Szexi Kaméleon becenéven. Ebben a cikkben arról fogok beszélni, hogyan csinálták. Reméljük, hogy a következő tíz percben legalább egy érdekes történetet olvashatsz, és legfeljebb találsz benne valami hasznosat, amit a munkádban is alkalmazni tudsz.

De kezdjük egy kis kontextussal.

A verseny és feltételei

A résztvevők fő feladata tehát egy vagy több javasolt intelligens szerződés megvalósítása, valamint a TON ökoszisztéma fejlesztésére vonatkozó javaslatok megfogalmazása volt. A verseny szeptember 24-től október 15-ig tartott, az eredményhirdetésre csak november 15-én került sor. Elég hosszú ideig, tekintve, hogy ezalatt a Telegramnak sikerült megtartania és eredményét hirdetnie a C++ nyelvű alkalmazások tervezésére és fejlesztésére a Telegram VoIP-hívások minőségének tesztelésére és értékelésére.

Két okosszerződést választottunk ki a szervezők által javasolt listából. Az egyikhez a TON-nal 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 a Haskellbe építettek.

A funkcionális programozási nyelv kiválasztása nem véletlen. Miénkben céges blog Gyakran beszélünk arról, hogy miért gondoljuk túlzásnak a funkcionális nyelvek összetettségét, és általában miért részesítjük előnyben ezeket az objektumorientáltakkal szemben. Egyébként azt is tartalmazza e cikk eredetije.

Miért is döntöttünk úgy, hogy részt veszünk?

Röviden azért, mert szakterületünk nem szabványos és összetett projektek, amelyek speciális készségeket igényelnek, és gyakran tudományos értéket képviselnek az informatikai közösség számára. Határozottan támogatjuk a nyílt forráskódú fejlesztést, részt veszünk annak népszerűsítésében, valamint együttműködünk vezető orosz egyetemekkel a számítástechnika és a matematika területén.

A verseny érdekes feladatai és a szeretett Telegram projektünkbe való bekapcsolódás önmagában is kiváló motivációt jelentett, de a nyereményalap további ösztönzést jelentett. 🙂

TON blokklánc kutatás

Szorosan figyelemmel kísérjük a blokklánc, a mesterséges intelligencia és a gépi tanulás új fejlesztéseit, és igyekszünk egyetlen jelentős kiadást sem kihagyni minden olyan területen, ahol dolgozunk. Így a verseny kezdetekor csapatunk már ismerte az ötleteket TON fehér papír. A TON-nal való munka megkezdése előtt azonban nem elemeztük a műszaki dokumentációt és a platform tényleges forráskódját, így az első lépés teljesen kézenfekvő volt - a hivatalos dokumentáció alapos tanulmányozása Online és projekttárak.

Mire a verseny elkezdődött, a kód már megjelent, í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ókat. Sajnos ez nem hozott eredményt – az Ubuntu platform összeállítására vonatkozó utasításokon kívül más anyagot nem találtunk.

Maga a dokumentáció alaposan átkutatott volt, de egyes területeken nehezen volt olvasható. Gyakran vissza kellett térnünk bizonyos pontokhoz, és az absztrakt ötletek magas szintű leírásairól az alacsony szintű megvalósítási részletekre váltani.

Könnyebb lenne, ha a specifikáció egyáltalán nem tartalmazná a megvalósítás részletes leírását. A virtuális gépek veremének mikéntjére vonatkozó információk nagyobb valószínűséggel vonják el a fejlesztők figyelmét, akik intelligens szerződéseket hoznak létre a TON platformhoz, mint hogy segítsenek nekik.

Nix: a projekt összeállítása

A Serokellnél nagy rajongók vagyunk Semmi. Ezzel gyűjtjük össze a projektjeinket és segítségével telepítjük NixOps, és telepítve van az összes szerverünkre Nix operációs rendszer. Ennek köszönhetően minden buildünk reprodukálható, és minden olyan operációs rendszeren működik, amelyre a Nix telepíthető.

Tehát az alkotással kezdtük Nix overlay kifejezéssel a TON összeállításhoz. Segítségével a TON összeállítása a lehető legegyszerűbb:

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

Vegye figyelembe, hogy nem kell semmilyen függőséget telepítenie. A Nix varázsütésre mindent megtesz helyetted, függetlenül attól, hogy NixOS-t, Ubuntut vagy macOS-t használsz.

TON programozása

A TON hálózat intelligens szerződéskódja a TON virtuális gépen (TVM) fut. A TVM összetettebb, mint a legtöbb más virtuális gép, és nagyon érdekes funkciókkal rendelkezik, például használható folytatások и adatokra mutató hivatkozások.

Ezenkívül a TON srácai három új programozási nyelvet hoztak létre:

Ötödik egy univerzális verem programozási nyelv, amely hasonlít Tovább. Szuper képessége a TVM-mel való interakció képessége.

FunC egy intelligens szerződéses programozási nyelv, amely hasonló a C és egy másik nyelvre van fordítva - Fift Assembler.

Ötödik összeszerelő — Fift könyvtár bináris végrehajtható kód generálásához a TVM számára. A Fifth Assemblernek nincs fordítója. Ez Embedded Domain Specific Language (eDSL).

A versenyünk működik

Végül itt az ideje, hogy megnézzük erőfeszítéseink eredményeit.

Aszinkron fizetési csatorna

A fizetési csatorna egy intelligens szerződés, amely lehetővé teszi, hogy két felhasználó fizetéseket küldjön a blokkláncon kívül. Ennek eredményeként nemcsak pénzt takarít meg (nincs jutalék), hanem időt is (nem kell megvárnia a következő blokk feldolgozását). A kifizetések olyan kicsik lehetnek, amennyire csak kívánják, és olyan gyakran, ahányszor szükséges. Ebben az esetben a feleknek nem kell bízniuk egymásban, hiszen a végelszámolás tisztességességét az okosszerződés garantálja.

Meglehetősen egyszerű megoldást találtunk a problémára. Két fél válthat aláírt üzeneteket, amelyek mindegyike két számot tartalmaz – a felek által fizetett teljes összeget. Ez a két szám így működik vektoros óra hagyományos elosztott rendszerekben, és állítsa be a "megtörtént korábban" sorrendet a tranzakciókra. Ezen adatok felhasználásával a szerződés képes lesz feloldani az esetleges konfliktusokat.

Valójában egy szám is elég az ötlet megvalósításához, de mindkettőt elhagytuk, mert így kényelmesebb felhasználói felületet tudtunk készíteni. Ezenkívül úgy döntöttünk, hogy minden üzenetben feltüntetjük a fizetés összegét. Enélkül, ha az üzenet valamilyen okból elveszett, akkor bár az összes összeg és a végső számítás helyes lesz, a felhasználó nem veszi észre a veszteséget.

Ötletünk teszteléséhez példákat kerestünk egy ilyen egyszerű és tömör fizetési csatorna protokoll használatára. Meglepő módon csak kettőt találtunk:

  1. Leírás hasonló megközelítés, csak egyirányú csatorna esetén.
  2. oktatóanyag, amely ugyanazt az ötletet írja le, mint a miénk, de anélkül, hogy sok fontos részletet ismertetne, mint például az általános helyesség és a konfliktusmegoldási eljárások.

Világossá vált, hogy van értelme jegyzőkönyvünket részletesen ismertetni, különös figyelmet fordítva annak helyességére. Több iteráció után elkészült a specifikáció, és most már te is megteheted. nézz rá.

A szerződést FunC-ben valósítottuk meg, és a szerződésünkkel való interakcióhoz szükséges parancssori segédprogramot teljes egészében Fiftben írtuk meg, a szervezők ajánlása szerint. Bármilyen más nyelvet is választhattunk volna a CLI-nkhöz, de érdekelt minket, hogy kipróbáljuk a Fitt, hogy megnézzük, hogyan működik a gyakorlatban.

Őszintén szólva, miután a Fifttel dolgoztunk, nem láttunk nyomós okot arra, hogy ezt a nyelvet előnyben részesítsük a népszerű és aktívan használt nyelvekkel szemben, fejlett eszközökkel és könyvtárakkal. Verem alapú nyelven programozni elég kellemetlen, hiszen folyamatosan fejben kell tartani, hogy mi van a veremen, és a fordító ebben nem segít.

Ezért véleményünk szerint a Fift létezésének egyetlen igazolása a Fift Assembler gazdanyelveként betöltött szerepe. De nem lenne jobb a TVM assemblert beágyazni valamelyik meglévő nyelvbe, mint egy újat kitalálni erre a lényegében egyetlen célra?

TVM Haskell eDSL

Itt az ideje, hogy beszéljünk a második intelligens szerződésünkről. Úgy döntöttünk, hogy több aláírást tartalmazó pénztárcát fejlesztünk, de egy újabb intelligens szerződés megírása a FunC-ben túl unalmas lenne. Szerettünk volna hozzá némi ízt adni, és ez volt a saját assembly nyelvünk a TVM számára.

A Fift Assemblerhez hasonlóan az új nyelvünk is beágyazott, de a Fift helyett a Haskell-t választottuk gazdagépnek, így teljes mértékben kiaknázhatjuk fejlett típusrendszerének előnyeit. Intelligens szerződésekkel végzett munka során, ahol egy kis hiba költsége is nagyon magas lehet, a statikus gépelés véleményünk szerint nagy előnyt jelent.

Hogy bemutassuk, hogyan néz ki a TVM assembler a Haskellbe ágyazva, egy szabványos pénztárcát alkalmaztunk rajta. Íme néhány dolog, amire figyelni kell:

  • Ez a szerződés egy funkcióból áll, de tetszőleges számú funkciót használhat. Ha új funkciót definiál a gazdagép nyelvén (azaz Haskell), az eDSL-ünk lehetővé teszi, hogy eldöntse, hogy azt külön rutinná kívánja-e tenni a TVM-ben, vagy egyszerűen beépítve a hívási pontba.
  • A Haskellhez hasonlóan a függvényeknek is vannak olyan típusai, amelyeket fordításkor ellenőrzünk. Az eDSL-ben a függvény bemeneti típusa az a verem, amelyet a függvény vár, az eredmény típusa pedig a hívás után előállított verem típusa.
  • A kód megjegyzésekkel rendelkezik stacktype, amely leírja a hívási pontnál várható veremtípust. Az eredeti pénztárcaszerződésben ezek csak megjegyzések voltak, de az eDSL-ünkben valójában a kód részét képezik, és fordításkor ellenőrzik. Dokumentációként vagy nyilatkozatként szolgálhatnak, amelyek segítenek a fejlesztőnek megtalálni a problémát, ha a kód és a verem típusa megváltozik. Természetesen az ilyen megjegyzések nem befolyásolják a futásidejű teljesítményt, mivel a rendszer nem generál hozzájuk TVM-kódot.
  • Ez még egy két hét alatt megírt prototípus, szóval még rengeteg munka van a projekten. Például az alábbi kódban látható osztályok összes példányát automatikusan generálni kell.

Így néz ki a multisig pénztárca megvalósítása eDSL-ünkön:

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

Az eDSL és több aláírású pénztárcaszerződésünk teljes forráskódja megtalálható a címen ezt az adattárat. És több részletesen elmondta a beépített nyelvekről kollégánk, Georgy Agapov.

Következtetések a versenyről és a TON-ról

A munkánk összesen 380 órát vett igénybe (beleértve a dokumentáció megismerését, megbeszéléseket és a tényleges fejlesztést). A versenyprojektben öt fejlesztő vett részt: CTO, csapatvezető, blockchain platform specialisták és Haskell szoftverfejlesztők.

Nehézség nélkül találtunk forrásokat a versenyen való részvételhez, hiszen a hackathon szellemisége, a szoros csapatmunka és az új technológiákban való gyors elmélyülés mindig izgalmas. Több álmatlan éjszakát a maximális eredmény elérése érdekében korlátozott erőforrások mellett felbecsülhetetlen tapasztalatokkal és kiváló emlékekkel ellensúlyoznak. Ezen túlmenően, az ilyen feladatokon végzett munka mindig jó próbája a vállalati folyamatoknak, hiszen rendkívül nehéz igazán tisztességes eredményeket elérni jól működő belső interakció nélkül.

A szöveget félretéve: lenyűgözött minket a TON csapata által végzett munka. Sikerült egy összetett, szép, és ami a legfontosabb, működő rendszert felépíteniük. A TON nagy lehetőségeket rejtő platformnak bizonyult. Ahhoz azonban, hogy ez az ökoszisztéma fejlődjön, sokkal többet kell tenni, mind a blokklánc-projektekben való felhasználása, mind a fejlesztési eszközök fejlesztése terén. Büszkék vagyunk arra, hogy most ennek a folyamatnak a részesei lehetünk.

Ha a cikk elolvasása után továbbra is kérdései vannak vagy ötletei vannak a TON használatával kapcsolatos problémák megoldására, írj nekünk – szívesen megosztjuk tapasztalatainkat.

Forrás: will.com

Hozzászólás