Bizonyára hallott már arról, hogy a Telegram De talán lemaradt arról a hírről, hogy nemrégiben a Telegram 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 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 .
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 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 és a .
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 . Mi ezeket a projekteket NixOps segítségével állítjuk elő és telepítjük . Ennek köszönhetően minden build reprodukálható és bármilyen operációs rendszeren működik, amelyre Nix telepíthető. Ezért elkezdtük létrehozni a
Nix overlay-t a TON összegyűjtésével kapcsolatos kifejezéssel. $ 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 Nyú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 é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 .
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 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:
- hasonló megközelítést, csak az egyirányú csatorna esetére.
- , 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 .
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 @Word32A teljes forráskódunkat az eDSL-ről és a multisign wallet szerződésről megtalálja Egy részletesebb 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, — örömmel megosztjuk tapasztalatainkat.
Forrás: habr.com
