Ju keni dëgjuar me siguri se Telegram . Por ndoshta keni humbur lajmin se së fundmi Telegram për implementimin e një ose më shumë kontratave të mençura për këtë platformë.
Ekipi Serokell me përvojë të pasur në zhvillimin e projekteve të mëdha blockchain nuk mund të qëndronte indiferent. Ne deleguam pesë punonjës në konkurs, dhe pas dy javësh ata e fituan atë vend të parë me një emër (në)esk modest, Sexy Chameleon. Në këtë artikull, do të flas për mënyrën si ia dolën. Kemi shpresë se për dhjetë minutat e ardhshme, ju do të lexoni të paktën një histori interesante, dhe më shumë mund të gjeni diçka të dobishme që mund ta aplikoni në punën tuaj.
Por le të fillojmë me një përqasje të vogël në kontekst.
Konkursi dhe kushtet e tij
Pra, detyrat kryesore të pjesëmarrësve ishin implementimi i një ose më shumë të kontratave të propozuara të mençura, si dhe paraqitja e propozimeve për përmirësimin e ekosistemit TON. Konkursi u mbajt nga 24 Shtatori deri më 15 Tetor dhe rezultatet u shpallën vetëm më 15 Nëntor. Mjaft e gjatë, duke marrë parasysh se gjatë kësaj periudhe Telegram kishte arritur të zhvillonte dhe shpallte rezultatet e konkurseve për dizajn dhe zhvillim aplikacionesh në C++ për testimin dhe vlerësimin e cilësisë së thirrjeve VoIP në Telegram.
Ne zgjodhëm nga lista e propozuar nga organizatorët dy kontrata të mençura. Për një prej tyre përdorëm mjetet që shpërndahen me TON, ndërsa të dytën e realizuam në një gjuhë të re, të zhvilluar nga inxhinierët tanë veçmas për TON dhe të integruar në Haskell.
Zgjedhja e një gjuhe funksionale programimi nuk është rastësore. Në blogun tonë ne shpesh flasim për arsyet pse e konsiderojmë kompleksitetin e gjuhëve funksionale si një teprim të madh, dhe pse në përgjithësi i preferojmë ato mbi ato të orientuara nga objekti. Sidoqoftë, në të ka edhe .
Pse vendosëm të marrim pjesë
Në përmbledhje, sepsespecializimi ynë është në projekte jo standarde dhe komplekse, që kërkojnë aftësi të veçanta dhe shpesh përbëjnë vlerë shkencore për komunitetin IT. Ne e mbështesim me pasion zhvillimin open-source dhe merremi me popullarizimin e tij, dhe gjithashtu bashkëpunojmë me universitetet kryesore të Rusisë në fushën e shkencave kompjuterike dhe matematikës.
Sfidhi konkurrencës dhe angazhimi në projektin që e duam shumë, Telegrami, ishte një motivim i shkëlqyer dhe fondi i çmimeve ishte një stimulus shtesë. 🙂
Kërkimi mbi blockchain-in TON
Ne monitorojmë me vëmendje zhvillimet e reja në blockchain, inteligjencën artificiale dhe mësimin e makinerive dhe përpiqemi të mos humbasim asnjë publikim të rëndësishëm në secilën nga fushat në të cilat punojmë. Prandaj, në momentin e fillimit të konkurrencës, ekipi ynë tashmë ishte njohur me idetë nga . Megjithatë, deri në fillimin e punës me TON, ne nuk kishim analizuar dokumentacionin teknik dhe kodin burimor të platformës, prandaj hapi i parë ishte mjaft i qartë - një studim i thellë i dokumentacionit zyrtar në dhe në .
Në fillim të konkurrencës, kodi tashmë ishte publikuar, prandaj, për të kursyer kohë, vendosëm të kërkojmë një udhëzues ose përmbledhje të shkruar nga përdoruesit. Fatkeqësisht, kjo nuk ishte shumë rezultative - përveç udhëzimit për ndërtimin e platformës në Ubuntu, nuk gjetëm materiale të tjera.
Dokumentacioni vetë rezultoi të ishte i punuar me kujdes, por leximi i tij në disa momente ishte i vështirë. Mjaft shpesh na duhej të ktheheshim në pika të ndryshme dhe të kalonim nga përshkrime të nivelit të lartë të ideve abstrakte në detaje të nivelit të ulët të realizimit.
Do të ishte më e lehtë nëse specifikimi nuk do të kishte ndonjë përshkrim të detajuar të realizimit. Informacioni se si mašina virtuale paraqet stekën e saj, më tepër e shpërqendron zhvilluesit që krijojnë kontrata inteligente për platformën TON, sesa i ndihmon ata.
Nix: ndërtimi i projektit
Në Serokell ne jemi adhurues të mëdhenj të . Ne i ndjejmë projektet tona me të dhe i zbatojmë ato me , dhe në të gjitha serverët tanë është instaluar . Kjo na lejon që të gjitha ndërtimet tona të jenë të riprodhueshme dhe të funksionojnë në çdo sistem operativ, në të cilin mund të instalohet Nix.
Prandaj, filluam me krijimin e . Me ndihmën e tij, të kompilosh TON është shumë e lehtë:
$ cd ~/ .config/ nixpkgs/ overlays && git clone https://github.com/serokell/ton.nix
$ cd /path/to/ton/repo && nix-shell
[nix-shell]$ cmakeConfigurePhase && makeVini re, nuk keni nevojë të instaloni asnjë varësi. Nix do të bëjë çdo gjë magjikisht për ju pa marrë parasysh nëse përdorni NixOS, Ubuntu ose macOS.
Programimi për TON
Kodi i kontratave të mençura në Rrjetin TON ekzekutohet në TON Virtual Machine (TVM). TVM është më e ndërlikuar se shumica e makinave të tjera virtuale dhe ka funksionalitete shumë interesante, për shembull, ajo mund të punojë me vazhdate (continuations) dhe lidhet me të dhëna.
Për më tepër, djemtë nga TON krijuan tre gjuhë të reja programimi:
Fift — një gjuhë universale programuese me gjendje (stack-based), që i ngjan . Superfuqia e saj është mundësia për t'u ndërvepruar me TVM.
FunC — një gjuhë programimi për kontrata të mençura që ngjason me dhe kompilohet në një gjuhë tjetër — Fift Assembler.
Fift Assembler — një bibliotekë Fift për gjenerimin e kodit ekzekutiv binar për TVM. Fift Assembler nuk ka një kompilator. Ky është .
Punimet tona konkurente
Më në fund, erdhi koha të shikojmë rezultatet e përpjekjeve tona.
Kanalet e pagesave asinkrone
Kanalet e pagesave (payment channels) janë kontrata të mençura që lejojnë dy përdorues të dërgojnë pagesa jashtë blockchain-it. Si rezultat, jo vetëm që kurseni para (nuk ka komision), por edhe kohë (nuk keni nevojë të prisni në procesin e bllokut tjetër). Pagesat mund të jenë sa të vogla dhe të ndodhin aq shpesh sa është e nevojshme. Në të njëjtën kohë, palët nuk është e nevojshme të besojnë njëra-tjetrën, pasi drejtësia e llogaritjes përfundimtare garantohet nga kontrata e mençur.
Kemi gjetur një zgjidhje të mjaftueshme për problemin. Dy palët mund të shkëmbejnë mesazhe të nënshkruara, secili prej të cilëve përmban dy numra — shumën totale të paguar nga secili nga pjesëmarrësit. Këta dy numra funksionojnë si në sistemet e shpërndara tradicionale dhe përcaktojnë rendin "ka ndodhur para" në transaksione. Duke përdorur këto të dhëna, kontrata do të jetë në gjendje të zgjidhë çdo konflikt të mundshëm.
Në të vërtetë, për realizimin e kësaj ideje mjafton edhe një numër, por ne e lamë të dyja, pasi në këtë mënyrë mundëm të krijojmë një ndërfaqe më të rehatshme për përdoruesin. Përveç kësaj, vendosëm që të përfshijmë në çdo mesazh madhësinë e pagesës. Pa të, nëse mesazhi humbet për ndonjë arsye, megjithëse të gjitha shumat dhe llogaritja përfundimtare do të ishin të sakta, përdoruesi mund të mos vërehet humbjen.
Për të verifikuar idenë tonë, ne kërkuam shembuj të përdorimit të një protokolli të tillë të thjeshtë dhe të qartë të kanalit të pagesës. Për habinë tonë, gjetëm vetëm dy:
- një qasje të ngjashme, vetëm për rastin e një kanali njëdrejtues.
- , në të cilin përshkruhet po e njëjta ide si e jona, vetëm pa shpjegimin e detajeve të shumta të rëndësishme, si korrektësia e përgjithshme dhe procedura e zgjidhjes së konflikteve.
U bë e qartë se ka kuptim të përshkruajmë në detaje protokollin tonë, duke i kushtuar vëmendje të veçantë korrektësisë së tij. Pas disa iteracioneve, specifikimi ishte gati, dhe tani ju gjithashtu mund të .
Ne realizuam kontratën në FunC, ndërsa utilitarin e linjës së komandës për të ndërvepruar me kontratën tonë e kemi shkruar plotësisht në Fift, ashtu siç rekomanduan organizatorët. Ne mund të kishim zgjedhur çfarëdo gjuhe tjetër për CLI-në tonë, por na kishte interes ta provonim pikërisht Fift, për të parë si do t'i shfaqej vetja në praktikë.
Sinqerisht, pasi punuam me Fift, ne nuk pamë arsye të forta për ta preferuar këtë gjuhë ndaj gjuhëve më popullore dhe aktivisht të përdorura me mjete dhe biblioteka të zhvilluar. Programimi në një gjuhë me të dhëna është mjaft i dhimbshëm, pasi duhet të mbash gjithmonë mend se çfarë ndodhet ku në të dhënat, dhe kompajleri nuk ndihmon në këtë.
Prandaj, e vetmja justifikim, sipas mendimit tonë, për ekzistencën e Fift është roli i tij si gjuhë host për Fift Assembler. Por nuk do të ishte më mirë të integrohej asmbler TVM në ndonjë gjuhë ekzistuese, përpara se të shpiknim një të re për këtë qëllim, në thelb, të vetëm?
TVM Haskell eDSL
Tani ka ardhur koha të flasim për kontratën tonë të dytë të zgjuar. Ne vendosëm të zhvillojmë një portofol me shumë nënshkrime, por të shkruajmë një tjetër kontratë të zgjuar në FunC do të ishte tepër e mërzitshme. Na pëlqente të shtonim ndonjë element të veçantë, dhe ai ishte gjuha jonë e vetme e asmblerit për TVM.
Ashtu si Fift Assembler, gjuha jonë e re është e integrueshme, vetëm se si host në vend të Fift, ne zgjodhëm Haskell, që na lejoi të shfrytëzojmë plotësisht sistemin e tij të avancuar të tipeve. Kur punoni me kontratat e zgjuara, ku çmimi i një gabimi të vogël mund të jetë shumë i lartë, tipizimi statik, sipas mendimit tonë, është një avantazh i madh.
Për të demonstruar se si duket assembleri TVM, i integruar në Haskell, ne realizuam një portofol standard. Ja disa gjëra për të cilat duhet të keni parasysh:
- Ky kontratë përbëhet nga një funksion, por ju mund të përdorni sa më shumë që dëshironi. Kur përcaktoni një funksion të ri në gjuhën e hostit (dmth. në Haskell), eDSL jonë ju lejon të zgjidhni nëse dëshironi që ai të shndërrohet në një nënprogram të veçantë në TVM ose thjesht të integrohet në vendin e thirrjes.
- Si në Haskell, funksionet kanë tipe që kontrollohen gjatë kompaktimit. Në eDSL tonë, tipi i hyrjes së funksionit është tipi i shtakës që funksioni pret, ndërsa tipi i rezultatit është tipi i shtakës që do të rezultojë pas thirrjes.
- Në kod ka annotime
stacktype, të cilat përshkruajnë tipi e pritur të shtakës në pikën e thirrjes. Në kontratën origjinale të portofolit, këto ishin thjesht komente, por në eDSL tonë ato në fakt janë pjesë e kodit dhe kontrollohen gjatë kompaktimit. Ato mund të shërbejnë si dokumentacion ose deklarata që ndihmojnë programuesin të gjejë problemin nëse, gjatë ndryshimeve në kod, tipi i shtakës ndryshon. Natyrisht, këto annotime nuk ndikojnë në performancën gjatë ekzekutimit, pasi asnjë kod TVM nuk gjenerohet për to. - Ky është ende një prototip, i shkruar brenda dy javëve, kështu që ka ende shumë punë për të bërë në projekt. Për shembull, të gjitha instancat e klasave që shihni në kodin më poshtë duhet të gjenerohen automatikisht.
Këtu është si duket realizimi i portofolit multisig në eDSL tonë:
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 @Word32Kodu i plotë burimor i eDSL tonë dhe kontrata e portofolit me shumë nënshkrim mund të gjeni në Një më të për gjuhët e integruara kolegu ynë Georgiy Agapov.
Konkluzionet për garën dhe TON
Në total, puna jonë zuri 380 orë (duke përfshirë njohjen me dokumentacionin, takimet dhe zhvillimin e drejtpërdrejt). Në projektin e konkursit morën pjesë pesë zhvillues: CTO, lideri i ekipit, specialistët e platformave blockchain dhe zhvilluesit e softuerit në Haskell.
Burimet për pjesëmarrjen në konkurs i gjetëm lehtësisht, pasi fryma e hackathon-it, puna e ngushtë në ekip, nevoja për të hyrë shpejt në aspektet e teknologjive të reja - gjithmonë është emocionuese. Disa net pa gjumë për të arritur rezultate maksimale në kushte burimesh të kufizuara kompenzohen nga përvoja të paçmuara dhe kujtime të shkëlqyera. Për më tepër, puna mbi këto detyra është gjithmonë një verifikim i mirë i proceseve të kompanisë, pasi të arrish rezultate të vërteta të denja pa një ndërveprim të brendshëm të përshtatshëm është jashtëzakonisht e vështirë.
Përveç lirizmit: ne ishim të impresionuar nga volumi i punës së kryer nga ekipi TON. Ata arritën të ndërtuan një sistem të ndërlikuar, të bukur dhe, mbi të gjitha, funksional. TON e tregoi veten si një platformë me potencial të madh. Megjithatë, për të zhvilluar këtë ekosistem, duhet të bëhet ende shumë, si sa i përket përdorimit të tij në projektet blockchain, ashtu edhe në përmirësimin e mjeteve të zhvillimit. Ne jemi krenarë që tani jemi pjesë e këtij procesi.
Nëse pas leximit të këtij artikulli ju kanë mbetur ndonjë pyetje ose keni ide se si të aplikoni TON për zgjidhjen e detyrave tuaja, — ne do të jemi të lumtur të ndihmojmë me përvojën tonë.
Burimi: habr.com
