Du har sikkert hørt om, at Telegram . Men du kunne have overset nyheden om, at Telegram for implementering af en eller flere smart contracts til denne platform.
Teamet hos Serokell, med stor erfaring i udviklingen af store blockchain-projekter, kunne ikke lade være med at deltage. Vi sendte fem medarbejdere til konkurrencen, og allerede to uger senere indtog de førstepladsen under det (u)beskedne tilfældige alias Sexy Chameleon. I denne artikel vil jeg fortælle, hvordan de gjorde det. Vi håber, at du i løbet af de næste ti minutter vil læse en interessant historie, og måske finde noget nyttigt, som du kan anvende i dit arbejde.
Men lad os begynde med et lille dyk ind i konteksten.
Konkurrencen og dens betingelser
Så hovedopgaverne for deltagerne var implementeringen af en eller flere af de foreslåede smart contracts samt at komme med forslag til forbedring af TON-økosystemet. Konkurrencen løb fra 24. september til 15. oktober, og resultaterne blev først offentliggjort den 15. november. Temmelig lang tid, når man overvejer, at Telegram i mellemtiden nåede at gennemføre og offentliggøre resultaterne af designkonkurrence og udviklingen af applikationer i C++ til testning og vurdering af kvaliteten af VoIP-opkald i Telegram.
Vi valgte to smart contracts fra listen, der blev foreslået af arrangørerne. Til den ene brugte vi værktøjer, der blev distribueret sammen med TON, og den anden blev implementeret på et nyt sprog, som vores ingeniører udviklede specielt til TON og indarbejdede i Haskell.
Valget af funktionelt programmeringssprog er ikke tilfældigt. I vores fortæller vi ofte, hvorfor vi mener, at kompleksiteten af funktionelle sprog er en stor overdrivelse, og hvorfor vi generelt foretrækker dem frem for objektorienterede. For resten er der også en .
Hvorfor besluttede vi overhovedet at deltage
Kort sagt, fordi vores specialisering er usædvanlige og komplekse projekter, der kræver særlige færdigheder og ofte repræsenterer videnskabelig værdi for IT-samfundet. Vi bakker varmt op om open-source udvikling og arbejder på at popularisere det, samt samarbejder med førende universiteter i Rusland inden for computer science og matematik.
De interessante opgaver i konkurrencen og tilknytningen til det projekt, vi holder så meget af, Telegram, var i sig selv en fantastisk motivation, og præmiepuljen blev et ekstra incitament. 🙂
Undersøgelse af TON blockchain
Vi holder nøje øje med nye udviklinger inden for blockchain, kunstig intelligens og maskinindlæring, og vi forsøger ikke at gå glip af en eneste betydningsfuld udgivelse inden for hver af de områder, vi arbejder med. Derfor var vores team allerede bekendt med ideerne fra . Dog havde vi ikke analyseret den tekniske dokumentation og den faktiske kildekode til platformen, før vi begyndte at arbejde med TON, så det første skridt var ret åbenlyst — en grundig undersøgelse af den officielle dokumentation på og i .
Ved begyndelsen af konkurrencen var koden allerede offentliggjort, så for at spare tid besluttede vi at lede efter en vejledning eller et resume skrevet af brugere. Desværre gav dette ikke noget resultat — vi fandt ikke andet end instruktioner til at samle platformen på Ubuntu.
Selve dokumentationen viste sig at være grundigt udarbejdet, men det var besværligt at læse den i nogle tilfælde. Ret ofte måtte vi vende tilbage til forskellige punkter og skifte fra højniveaubeskrivelser af abstrakte ideer til lavniveau detaljer om implementeringen.
Det ville have været lettere, hvis specifikationen slet ikke havde detaljerede beskrivelser af implementeringen. Oplysninger om, hvordan den virtuelle maskine repræsenterer sin stak, distraherer snarere udviklere, der skaber smarte kontrakter til platformen TON, end det hjælper dem.
Nix: vi samler projektet
Hos Serokell er vi store tilhængere . Vi samler vores projekter med dem og implementerer dem ved hjælp af , og der er installeret på alle vores servere. Takket være dette er alle vores builds reproducerbare og fungerer på ethvert operativsystem, som Nix kan installeres på.
Derfor begyndte vi med at oprette . Med det er det ekstremt enkelt at kompilere TON:
$ cd ~/.config/nixpkgs/overlays && git clone https://github.com/serokell/ton.nix
$ cd /path/to/ton/repo && nix-shell
[nix-shell]$ cmakeConfigurePhase && makeBemærk, at du ikke behøver at installere nogen afhængigheder. Nix vil magisk sørge for alt for dig, uanset om du bruger NixOS, Ubuntu eller macOS.
Programmering for TON
Koden for smarte kontrakter i TON Network udføres på TON Virtual Machine (TVM). TVM er mere kompleks end de fleste andre virtuelle maskiner og har en del interessante funktioner, såsom at den kan arbejde med fortsættelser (continuations) og datahenvisninger.
Desuden har folkene fra TON skabt hele tre nye programmeringssprog:
Fift — et universelt stak-baseret programmeringssprog, der minder om . Dets superkraft er muligheden for at interagere med TVM.
FunC — et programmeringssprog til smarte kontrakter, der ligner og kompileres til endnu et sprog — Fift Assembler.
Fift Assembler — en Fift-bibliotek til generering af binær eksekverbar kode til TVM. Fift Assembler har ikke en compiler. Det er .
Vores konkurrenceprojekter
Endelig er det tid til at se på resultaterne af vores bestræbelser.
Asynkront betalingskanal
En betalingskanal (payment channel) er en smart kontrakt, der giver to brugere mulighed for at sende betalinger uden for blockchainen. Dette sparer ikke kun penge (ingen gebyrer), men også tid (du behøver ikke vente på, at den næste blok behandles). Betalinger kan være så små som ønskes og ske så ofte som nødvendigt. Desuden er det ikke nødvendigt for parterne at stole på hinanden, da retfærdigheden i den endelige afregning garanteres af smart kontrakten.
Vi har fundet en ret simpel løsning på problemet. De to parter kan udveksle underskrevne meddelelser, hvoraf hver indeholder to tal — det samlede beløb betalt af hver deltager. Disse to tal fungerer som i traditionelle distribuerede systemer og angiver rækkefølgen "skete før" på transaktionerne. Ved at bruge disse data kan kontrakten løse enhver mulig konflikt.
Faktisk er det nok med et eneste tal for at implementere denne idé, men vi har efterladt begge, da vi derved kunne lave en mere brugervenlig grænseflade. Derudover besluttede vi at inkludere størrelsen af betalingen i hver meddelelse. Uden dette, hvis meddelelsen af en eller anden grund går tabt, vil brugeren måske ikke bemærke tabet, selvom alle beløb og den endelige afregning er korrekte.
For at teste vores idé, søgte vi efter eksempler på brugen af en så simpel og ligetil betalingskanalprotokol. Til vores overraskelse fandt vi kun to:
- en lignende tilgang, kun for en ensrettet kanal.
- , som beskriver den samme idé som vores, men uden forklaring af mange vigtige detaljer, såsom den overordnede korrekthed og proceduren til konfliktløsning.
Det blev klart, at det giver mening at beskrive vores protokol detaljeret med særlig vægt på dens korrekthed. Efter flere iterationer var specifikationen klar, og nu kan du også .
Vi implementerede kontrakten i FunC, og kommandolinjeværktøjet til at interagere med vores kontrakt skrev vi helt i Fift, som arrangørerne anbefalede. Vi kunne have valgt et andet sprog til vores CLI, men vi var nysgerrige efter at prøve Fift for at se, hvordan det ville præstere i praksis.
Ærligt talt, efter at have arbejdet med Fift, så vi ingen overbevisende grunde til at foretrække dette sprog fremfor mere populære og aktivt anvendte sprog med udviklede værktøjer og biblioteker. At programmere i et stakbaseret sprog er temmelig ubehageligt, da man konstant skal holde styr på, hvad der ligger hvor i stakken, og kompilereren hjælper ikke med dette.
Derfor mener vi, at den eneste rimelige grund til Fifts eksistens er dens rolle som værtsprog for Fift Assembler. Men ville det ikke have været bedre at indbygge TVM-assembleren i et eksisterende sprog i stedet for at finde på et nyt til dette i bund og grund unikke formål?
TVM Haskell eDSL
Nu er det tid til at fortælle om vores anden smarte kontrakt. Vi besluttede at udvikle en multisignaturtegneboks, men at skrive endnu en smart kontrakt i FunC ville være for kedeligt. Vi ønskede at tilføje noget specielt, og det blev vores eget assembler-sprog til TVM.
Ligesom Fift Assembler er vores nye sprog indlejret, men i stedet for Fift valgte vi Haskell som vært, hvilket gjorde os i stand til fuldt ud at udnytte dets avancerede typesystem. Når man arbejder med smarte kontrakter, hvor omkostningerne ved selv en lille fejl kan være meget høje, mener vi, at statisk typisering er en stor fordel.
For at demonstrere, hvordan TVM-assembleren indbygget i Haskell ser ud, har vi implementeret en standard tegnebog. Her er nogle ting, du skal være opmærksom på:
- Denne kontrakt består af én funktion, men du kan bruge så mange du vil. Når du definerer en ny funktion på værtsproget (det vil sige Haskell), giver vores eDSL dig mulighed for at vælge, om den skal blive til et separat underprogram i TVM eller blot indleires på stedet, hvor den bliver kaldt.
- Ligesom i Haskell har funktioner typer, der bliver kontrolleret under kompileringen. I vores eDSL er typen for funktionsindgangen den type stak, som funktionen forventer, mens typen for resultatet er den type stak, som vil resultere efter kaldet.
- Der er annoteringer i koden
stacktype, der beskriver den forventede staktype på kaldestedet. I den originale tegnebogskontrakt var de blot kommentarer, men i vores eDSL er de faktisk en del af koden og bliver kontrolleret under kompileringen. De kan fungere som dokumentation eller påstande, der hjælper udvikleren med at finde problemer, hvis staktypen ændres, når koden ændres. Selvfølgelig påvirker sådanne annoteringer ikke ydeevnen under udførelsen, da der ikke genereres nogen TVM-kode for dem. - Det er stadig en prototype, der er skrevet på to uger, så der er stadig meget arbejde at gøre på projektet. For eksempel skal alle klasseinstanser, som du ser i den nedenstående kode, genereres automatisk.
Her er, hvordan implementeringen af multisig-tegnebog ser ud i vores 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 @Word32Den fulde kildekode til vores eDSL og wallet kontrakt med multisignatur kan findes i En mere vores kollega Georgiy Agapov.
Konklusioner om konkurrencen og TON
I alt tog vores arbejde 380 timer (inklusive at sætte sig ind i dokumentationen, møder og selve udviklingen). Fem udviklere deltog i konkurrenceprojektet: CTO, teamleder, specialister i blockchain-platforme og softwareudviklere med Haskell.
Vi fandt let de ressourcer, der var nødvendige for at deltage i konkurrencen, da ånden fra hackathonet, tæt teamwork og nødvendigheden af hurtig indsigt i nye teknologiers aspekter altid er spændende. Flere søvnløse nætter for at opnå det maksimale resultat under begrænsede ressourcer kompenseres af uvurderlig erfaring og gode minder. Endvidere er arbejdet med sådanne opgaver altid en god test af virksomhedens processer, da det er ekstremt svært at opnå virkelig værdifulde resultater uden en velfungerende intern interaktion.
Udover lyrikken: vi var imponerede over mængden af arbejde, som TON-teamet havde udført. De formåede at bygge et komplekst, smukt, og vigtigst af alt, fungerende system. TON har vist sig at være en platform med stort potentiale. Men for at dette økosystem kan udvikle sig, er der stadig meget, der skal gøres, både med hensyn til dets anvendelse i blockchain-projekter og forbedring af udviklingsværktøjerne. Vi er stolte af at være en del af denne proces nu.
Hvis du efter at have læst denne artikel har spørgsmål eller ideer til, hvordan TON kan hjælpe dig med at løse dine opgaver, — vi deler gerne vores erfaring.
Kilde: habr.com
