Despre cum să scrii și să publici un smart contract în Telegram Open Network (TON)

Despre cum să scrii și să publici un smart contract în TON

Despre ce este acest articol?

În articol voi povesti despre cum am participat la primul (din două) concursuri Telegram pe blockchain, nu am ocupat un loc premiant și am decis să-mi notez experiența în articol, pentru a nu dispărea în uitare și, poate, pentru a ajuta pe cineva.

Fiindcă nu voiam să scriu cod abstract, ci să fac ceva funcțional, pentru articol am scris un smart contract pentru o loterie instantanee și un site care arată datele smart contractului direct din TON, fără a folosi depozite intermediare.

Articolul va fi util celor care vor să-și facă primul smart contract în TON, dar nu știu de unde să înceapă.

Pe exemplul loteriei, voi trece de la instalarea mediului până la publicarea smart contractului, interacțiunea cu acesta și voi scrie un site pentru obținerea și publicarea datelor.

Despre participarea la concurs

În octombrie anul trecut, Telegram a anunțat un concurs pe blockchain cu noi limbaje Fift și FunC. Era necesar să scrii la alegere oricare dintre cele cinci smart contracte propuse. Am considerat că ar fi bine să mă ocup de ceva neobișnuit, să studiez limbajul și să fac ceva, chiar dacă în viitor nu va trebui să scriu altceva. În plus, subiectul este mereu pe buzele tuturor.

Trebuie spus că nu aveam experiență în dezvoltarea smart contractelor.

Am plănuit să particip până la final, atâta timp cât este posibil și după să scriu un articol de revizuire, dar am eșuat imediat la primul. Am scris un portofel cu multiple semnături pe FunC și el, în general, a funcționat. M-am bazat pe un smart contract pe Solidity.

La acel moment, am considerat că este cu siguranță suficient pentru a ocupa măcar un loc premiant. În cele din urmă, aproximativ 40 din cei 60 de participanți au devenit câștigători, iar eu nu am fost printre ei. În general, nu este nimic înfricoșător în asta, dar m-a stresat un singur lucru. La momentul anunțării rezultatelor, nu fusese efectuat un review cu test pentru contractul meu; am întrebat participanții în chat dacă mai este cineva care nu îl are, și nu au fost astfel de persoane.

Se pare că, observând mesajele mele, judecătorii au publicat un comentariu în două zile, dar nu am înțeles dacă au omis din întâmplare contractul meu inteligent în timpul procesului de evaluare sau pur și simplu au considerat că este atât de slab încât nu necesită comentarii. Am pus o întrebare pe pagină, dar nu am primit niciun răspuns. Deși cine a judecat - nu este un secret, am considerat inutil să scriu mesaje personale.

A fost consumat un timp considerabil pentru a înțelege, așa că am decis să scriu un articol. Deoarece informațiile sunt încă destul de limitate, articolul va ajuta la economisirea timpului tuturor celor interesați.

Conceptul de funcționare a contractelor inteligente în TON

Înainte de a scrie ceva, trebuie să înțelegem din ce parte să ne apropiem de această chestiune. Așadar, acum voi explica din ce părți este alcătuit sistemul. Mai precis, ce părți trebuie să știm pentru a scrie măcar un contract funcțional.

Ne vom concentra pe scrierea unui contract inteligent și pe lucrul cu TON Virtual Machine (TVM), Fift și FunC, așa că articolul seamănă mai mult cu o descriere a dezvoltării unei programe obișnuite. Nu ne vom opri aici asupra felului în care funcționează platforma.

De fapt, despre cum funcționează TVM și limbajul Fift există o documentație oficială bună. Pe parcursul participării la concurs și acum, în timpul scrierii contractului actual, am apelat frecvent la aceasta.

Limbajul principal în care sunt scrise contractele inteligente este FunC. În prezent, nu există documentație disponibilă pentru acesta, așa că pentru a scrie ceva, trebuie să studiem exemple de contracte inteligente din repositoare oficiale și implementarea limbajului acolo, plus putem consulta exemplele de contracte inteligente din concursurile anterioare. Linkurile se află la sfârșitul articolului.

Să presupunem că am scris deja un contract inteligent în FunC, apoi compilăm codul în asamblator Fift.

Contractul inteligent compilat rămâne de publicat. Pentru asta trebuie să scriem o funcție în Fift, care va primi codul contractului inteligent și câteva alte parametrii ca intrare, iar ca ieșire obținem un fișier cu extensia .boc (ceea ce înseamnă "bag of cells"), iar, în funcție de cum vom scrie, cheia privată și adresa care este generată pe baza codului contractului inteligent. Pe adresa contractului inteligent, care nu a fost încă publicată, se pot trimite deja grame.

Pentru a publica un contract inteligent în TON, trebuie să obținem .boc Fișierul trebuie să fie trimis în blockchain cu ajutorul unui client ușor (despre care vom discuta mai jos). Însă înainte de a publica, trebuie să transferăm grame pe adresa generată, altfel contractul inteligent nu va fi publicat. După publicare, putem interacționa cu contractul inteligent, trimițându-i mesaje din exterior (de exemplu, folosind clientul ușor) sau din interior (de exemplu, un contract inteligent trimite unui alt contract un mesaj în interiorul TON).

După ce am înțeles cum se publică codul, devine mai simplu. Știm aproximativ ce vrem să scriem și cum va funcționa programul nostru. În timpul scrierii, căutăm cum a fost deja implementat în contractele inteligente existente, fie consultând codul din depozitul oficial, fie verificând documentația oficială. Fift și FunC În depozitul oficial.

Foarte des am căutat după cuvinte cheie în chat-ul Telegram unde s-au adunat toți participanții la concurs și angajații Telegram, de asemenea, s-a întâmplat că în timpul concursului toți s-au reunit acolo și au început să discute despre Fift și FunC. Linkul este la finalul articolului.

E timpul să trecem de la teorie la practică.

Pregătirea mediului pentru lucrul cu TON

Tot ce va fi descris în articol am făcut pe MacOS și am verificat din nou pe Ubuntu 18.04 LTS curat pe Docker.

Primul lucru pe care trebuie să-l facem este să descărcăm și să instalăm lite-client cu ajutorul căruia putem trimite cereri în TON.

Informațiile de pe site-ul oficial descriu destul de detaliat și clar procesul de instalare și omite unele detalii. Aici urmăm instrucțiunile, instalând în același timp dependențele lipsă. Nu am compilat eu fiecare proiect, ci am instalat din depozitul oficial Ubuntu (pe MacOS am folosit brew).

apt -y install git 
apt -y install wget 
apt -y install cmake 
apt -y install g++ 
apt -y install zlib1g-dev 
apt -y install libssl-dev 

După ce toate dependențele sunt instalate, putem instala lite-client, Fift, FunC.

Mai întâi clonăm depozitul TON împreună cu dependențele. Pentru comoditate, vom face totul în folderul ~/TON.

cd ~/TON
git clone https://github.com/ton-blockchain/ton.git
cd ./ton
git submodule update --init --recursive

În depozit se păstrează și implementările Fift și FunC.

Acum suntem pregătiți să compilăm proiectul. Codul din depozit a fost clonat în folderul ~/TON/ton. În ~/TON creăm un folder build și compilăm în el proiectul.

mkdir ~/TON/build 
cd ~/TON/build
cmake ../ton

Deoarece ne pregătim să scriem un contract inteligent, avem nevoie nu doar de lite-client, ci și Fift de FunC, așa că compilăm totul. Este un proces lent, așa că așteptăm.

cmake --build . --target lite-client
cmake --build . --target fift
cmake --build . --target func

Apoi descărcăm fișierul de configurare în care se află datele despre nodul la care lite-client se va conecta.

wget https://test.ton.org/ton-lite-client-test1.config.json

Facem primele solicitări în TON

Acum să pornim lite-client.

cd ~/TON/build
./lite-client/lite-client -C ton-lite-client-test1.config.json

Dacă compilarea a decurs bine, după pornire veți vedea jurnalul de conectare al clientului lite la nod.

[ 1][t 2][1582054822.963129282][lite-client.h:201][!testnode]   conexiune gata
[ 2][t 2][1582054823.085654020][lite-client.cpp:277][!testnode] versiunea serverului este 1.1, capabilități 7
[ 3][t 2][1582054823.085725069][lite-client.cpp:286][!testnode] timpul serverului este 1582054823 (delta 0)
...

Se poate executa comanda help și se pot vedea ce comenzi sunt disponibile.

help

Să enumerăm comenzile pe care le vom utiliza în acest articol.

listă de comenzi disponibile:
last    Obține informațiile despre ultimul bloc și stare de la server
sendfile  Încarcă un mesaj serializat din  și-l trimite serverului
getaccount  []  Încarcă cea mai recentă stare a contului specificat;  este în formatul [:]
runmethod  []  ...   Rulează metoda GET  a contului  cu parametrii specificați

last obține ultimul bloc creat de la server.

sendfile  trimite un fișier cu un mesaj în TON, exact prin această comandă se publică smart contractul și cererile către acesta.

getaccount  încarcă starea curentă a smart contractului cu adresa specificată.

runmethod  []    rulează metodele GET ale smart contractului. 

Acum suntem pregătiți să scriem contractul propriu-zis.

Implementarea

Ideea

După cum am menționat mai sus, smart contractul pe care îl scriem este o loterie.

Și nu este o loterie în care trebuie să cumperi un bilet și să aștepți o oră, o zi sau o lună, ci una instantanee în care utilizatorul trimite la adresa contractului N grami și primește imediat înapoi 2 * N grami sau pierde. Probabilitatea de câștig o vom face de aproximativ 40%. Dacă gramii pentru plată nu sunt suficienți, atunci vom considera tranzacția ca o completare.

De asemenea, este important ca pariurile să fie vizibile în timp real și într-un mod convenabil, astfel încât utilizatorul să poată înțelege imediat dacă a câștigat sau a pierdut. Așadar, trebuie să realizăm un site web care să arate pariurile și rezultatul direct din TON.

Scrierea smart contractului

Pentru comoditate, am realizat o evidență a codului pentru FunC, pluginul poate fi găsit și instalat în căutarea Visual Studio Code. Dacă doriți să adăugați ceva, am publicat pluginul în acces deschis. De asemenea, anterior a fost creat un plugin pentru lucrul cu Fift, care poate fi găsit și instalat în VSC.

Hai să creăm un repository unde vom comite rezultatele intermediare.

Pentru a ne ușura viața, vom scrie un smart contract și îl vom testa local, până când va fi gata. Abia după aceea îl vom publica în TON.

Smart contractul are două metode externe la care se poate apela. Prima, recv_external() această funcție se execută atunci când se face o solicitare către contract din lumea exterioară, adică nu din TON, de exemplu atunci când formăm noi un mesaj și îl trimitem prin lite-client. A doua, recv_internal() aceasta este atunci când un alt contract din TON apelează contractul nostru. În ambele cazuri, se pot transmite parametrii în funcție.

Să începem cu un exemplu simplu, care va funcționa dacă este publicat, dar nu are nicio funcționalitate.

() recv_internal(slice in_msg) impure {
    ;; TODO: implementare 
}

() recv_external(slice in_msg) impure {
    ;; TODO: implementare  
}

Aici trebuie să explicăm ce este slice. Toate datele stocate în TON Blockchain sunt o colecție de TVM cell sau simplu cell, într-o astfel de celulă se pot stoca până la 1023 de biți de date și până la 4 link-uri către alte celule.

TVM cell slice sau slice este o parte a unei celule existente cell care este folosită pentru parsarea acesteia, mai departe va fi clar. Ceea ce este important pentru noi este că putem transmite în smart contract slice și în funcție de tipul mesajului, putem procesa datele în recv_external() sau recv_internal().

impure — cuvânt cheie care indică faptul că funcția modifică datele smart contractului.

Să salvăm codul contractului în lottery-code.fc și să-l compilăm.

~\/TON\/build\/crypto\/func -APSR -o lottery-compiled.fif ~\/TON\/ton\/crypto\/smartcont\/stdlib.fc .\/lottery-code.fc 

Valorile flag-urilor pot fi vizualizate folosind comanda

~\/TON\/build\/crypto\/func -help

Am obținut un cod Fift-assembler compilat în lottery-compiled.fif:

// lottery-compiled.fif

"Asm.fif" include
// automatically generated from `/Users/rajymbekkapisev/TON/ton/crypto/smartcont/stdlib.fc` `./lottery-code.fc` 
PROGRAM{
  DECLPROC recv_internal
  DECLPROC recv_external
  recv_internal PROC:<{
    //  in_msg
    DROP    // 
  }>
  recv_external PROC:<{
    //  in_msg
    DROP    // 
  }>
}END>c

Acesta poate fi rulat local, pentru care vom pregăti mediul.

Observăm că, în prima linie, se include Asm.fif, acesta este cod scris în Fift pentru Fift-assembler.

Deoarece dorim să rulăm și să testăm smart contractul local, vom crea un fișier lottery-test-suite.fif și vom copia codul compilat în el, înlocuind ultima linie care scrie codul smart contractului într-o constantă code, pentru a-l transmite ulterior în mașina virtuală:

"TonUtil.fif" include
"Asm.fif" include

PROGRAM{
  DECLPROC recv_internal
  DECLPROC recv_external
  recv_internal PROC:<{
    \/\/  in_msg
    DROP    \/\/ 
  }>
  recv_external PROC:<{
    \/\/  in_msg
    DROP    \/\/ 
  }>
}END>s constant code

Până acum pare clar, acum adăugăm în același fișier codul pe care îl vom folosi pentru a rula TVM.

0 tuple 0x076ef1ea , \/\/ magic
0 , 0 , \/\/ actions msg_sents
1570998536 , \/\/ unix_time
1 , 1 , 3 , \/\/ block_lt, trans_lt, rand_seed
0 tuple 100000000000000 , dictnew , , \/\/ remaining balance
0 , dictnew , \/\/ contract_address, global_config
1 tuple \/\/ wrap to another tuple
constant c7

0 constant recv_internal \/\/ to run recv_internal() 
-1 constant recv_external \/\/ to invoke recv_external()

În c7 Înregistrăm contextul, adică datele cu care va fi lansat TVM (sau starea rețelei). De asemenea, în timpul concursului, unul dintre dezvoltatori a arătat cum se creează c7 și am copiat. În acest articol, s-ar putea să fie necesar să schimbăm rand_seed deoarece acesta influențează generarea unui număr aleatoriu și nu trebuie să se schimbe, astfel încât de fiecare dată să se returneze același număr.

recv_internal și recv_external constantelor cu valoarea 0 și -1 vor corespunde apelurilor funcțiilor respective în contractul inteligent.

Acum suntem pregătiți să creăm primul test pentru contractul nostru inteligent gol. Pentru claritate, deocamdată, toate testele le vom adăuga în acest fișier lottery-test-suite.fif.

Să creăm o variabilă storage și să scriem în ea un gol cell, acesta va fi stocarea contractului inteligent.

message acesta este mesajul pe care îl vom transmite contractului inteligent din exterior. De asemenea, îl vom lăsa gol deocamdată.

stocare variabilă 
<b b> stocare ! 

mesaj variabil 
<b b> mesaj ! 

După ce am pregătit constantele și variabilele, lansăm TVM folosind comanda runvmctx și transmitem parametrii creați ca input.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

În final, vom obține un cod intermediar de Acum putem rula codul obținut. Fift.

export FIFTPATH=~\/TON\/ton\/crypto\/fift\/lib \/\/ executăm o dată pentru confort ~\/TON\/build\/crypto\/fift -s lottery-test-suite.fif

Programul ar trebui să ruleze fără erori și în output vom vedea logul executării: 

execute SETCP 0 execute DICTPUSHCONST 19 (xC_,1) execute DICTIGETJMPZ execute DROP execute implicit RET [ 3][t 0][1582281699.325381279][vm.cpp:479] steps: 5 gas: used=304, max=9223372036854775807, limit=9223372036854775807, credit=0

Excelent, am scris prima versiune funcțională a contractului inteligent.

Acum trebuie să adăugăm funcționalitate. Mai întâi, ne vom ocupa de mesajele care vin din lumea externă în

Dezvoltatorul alege singur formatul mesajului pe care contractul îl poate accepta. recv_external()

Dar, de obicei,

în primul rând, dorim să protejăm contractul nostru de lumea externă și să ne asigurăm că doar proprietarul contractului poate trimite mesaje externe la acesta.

  • în al doilea rând, atunci când trimitem un mesaj valid în TON, dorim ca acest lucru să se întâmple o singură dată, iar la trimiterea repetată a aceluiași mesaj, contractul inteligent să îl respingă.
  • În al doilea rând, atunci când trimitem un mesaj valid în TON, dorim ca acest lucru să se întâmple exact o dată, iar la o re-trimitere a aceluiași mesaj, contractul inteligent să îl respingă.

Prin urmare, aproape fiecare contract abordează aceste două probleme, deoarece contractul nostru primește mesaje externe, trebuie să ne ocupăm și de acest lucru.

Vom face acest lucru în ordinea inversă. Mai întâi vom rezolva problema repetării, dacă contractul a primit deja un astfel de mesaj și l-a procesat, nu îl va executa a doua oară. Apoi, vom rezolva problema astfel încât doar un cerc restrâns de persoane să poată trimite mesaje contractului inteligent.

Există diferite modalități de a rezolva problema mesajelor repetate. Vom proceda astfel. În contractul inteligent, inițializăm un contor al mesajelor primite cu o valoare inițială de 0. Fiecare mesaj trimis contractului inteligent va include valoarea curentă a contorului. Dacă valoarea contorului din mesaj nu coincide cu valoarea din contractul inteligent, nu-l procesăm; dacă coincid, îl procesăm și incrementăm contorul din contractul inteligent cu 1.

Ne întoarcem la lottery-test-suite.fif și adăugăm un al doilea test. Vom trimite un număr incorect, codul ar trebui să arunce o excepție. De exemplu, să presupunem că în datele contractului este stocată 166, iar noi trimitem 165.

<b 166 32 u, b> stocare !
<b 165 32 u, b> mesaj !

mesaj @ 
recv_external 
code 
stocare @ 
c7 
runvmctx

așază 
exit_code ! 
."Cod de ieșire " exit_code @ . cr 
exit_code @ 33 - abort"Test #2 Necalificat"

Să lansăm.

 ~\/TON\/build\/crypto\/fift -s lottery-test-suite.fif 

Și vom vedea că testul se execută cu o eroare.

[ 1][t 0][1582283084.210902214][words.cpp:3046] lottery-test-suite.fif:67: abort": Test #2 nu a fost trecut
[ 1][t 0][1582283084.210941076][fift-main.cpp:196]      Eroare la interpretarea fișierului `lottery-test-suite.fif`: eroare în interpretarea fișierului inclus `lottery-test-suite.fif` : lottery-test-suite.fif:67: abort": Test #2 nu a fost trecut

În această etapă lottery-test-suite.fif ar trebui să arate ca la link.

Acum să scriem logica contorului în contractul inteligent în lottery-code.fc.

() recv_internal(slice in_msg) impure {
    ;; TODO: implementare 
}

() recv_external(slice in_msg) impure {
    if (slice_empty?(in_msg)) {
        return (); 
    }
    int msg_seqno = in_msg~load_uint(32);
    var ds = begin_parse(get_data());
    int stored_seqno = ds~load_uint(32);
    throw_unless(33, msg_seqno == stored_seqno);
}

În slice in_msg conține mesajul pe care îl trimitem.

Primul lucru pe care îl facem este să verificăm dacă mesajul conține date; dacă nu, ieșim pur și simplu.

Apoi, analizăm mesajul. in_msg~load_uint(32) încarcă numărul 165, pe 32 de biți unsigned int din mesajul trimis.

Apoi, încărcăm 32 de biți din stocarea contractului inteligent. Verificăm dacă numărul încărcat coincide cu cel trimis; dacă nu, aruncăm o excepție. În cazul nostru, deoarece trimitem un număr diferit, ar trebui să se arunce o excepție.

Acum să compilăm.

~\/TON\/build\/crypto\/func -APSR -o lottery-compiled.fif ~\/TON\/ton\/crypto\/smartcont\/stdlib.fc .\/lottery-code.fc 

Codul rezultat să-l copiem în lottery-test-suite.fif, fără a uita să înlocuim ultima linie.

Verificăm că testul trece:

~\/TON\/build\/crypto\/fift -s lottery-test-suite.fif

Iată aici poți vizualiza commitul corespunzător cu rezultatele curente.

Observăm că este incomod să copiem constant codul compilat al smart contractului într-un fișier cu teste, așa că vom scrie un script care va salva codul într-o constantă pentru noi, iar noi vom conecta pur și simplu codul compilat în testele noastre prin "include".

În folderul proiectului vom crea un fișier build.sh cu următorul conținut.

#!/bin/bash

~/TON/build/crypto/func -SPA -R -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc

Îl vom face executabil.

chmod +x ./build.sh

Acum, este suficient să rulăm scriptul nostru pentru a compila contractul. Dar, în plus, trebuie să-l salvăm într-o constantă code. Așadar, vom crea un nou fișier lotter-compiled-for-test.fif, pe care îl vom include în fișierul lottery-test-suite.fif.

Vom adăuga în scriptul sh codul care va pur și simplu duplifica fișierul compilat în lotter-compiled-for-test.fif și va schimba ultima linie din acesta.

# copy and change for test 
cp lottery-compiled.fif lottery-compiled-for-test.fif
sed '$d' lottery-compiled-for-test.fif > test.fif
rm lottery-compiled-for-test.fif
mv test.fif lottery-compiled-for-test.fif
echo -n "}END>s constant code" >> lottery-compiled-for-test.fif

Acum, pentru a verifica, să rulăm scriptul rezultat și ne va genera un fișier lottery-compiled-for-test.fif, pe care îl vom include în lottery-test-suite.fif

În lottery-test-suite.fif sterge codul contractului și adaugă linia "lottery-compiled-for-test.fif" include.

Rulăm testele pentru a verifica dacă trec.

~\/TON\/build\/crypto\/fift -s lottery-test-suite.fif

Excelent, acum, pentru a automatiza rularea testelor, creăm un fișier test.sh, care va executa mai întâi build.sh, apoi va rula testele.

touch test.sh
chmod +x test.sh

În interior scriem

./build.sh 

echo "nCompilarea a fost finalizatăn"

export FIFTPATH=~/TON/ton/crypto/fift/lib
~/TON/build/crypto/fift -s lottery-test-suite.fif

Vom face test.sh și vom rula pentru a ne asigura că testele funcționează.

chmod +x ./test.sh
./test.sh

Verificăm că contractul se compilează și testele se execută.

Excelent, acum la rulare test.sh compilarea și rularea testelor vor avea loc automat. Iată un link către commit.

Bine, înainte să continuăm, hai să facem încă un lucru pentru comoditate.

Vom crea un folder build unde vom păstra contractul compilat și copia sa salvată într-o constantă lottery-compiled.fif, lottery-compiled-for-test.fif. De asemenea, vom crea un folder test unde vor fi stocate fișierele cu teste lottery-test-suite.fif și potențial alte fișiere auxiliare. Link către modificările corespunzătoare.

Vom continua dezvoltarea smart contractului.

Ulterior, va trebui să existe un test care să verifice că mesajul este primit și că numărătorul este actualizat în stocare atunci când trimitem un număr corect. Dar vom face asta mai târziu.

Acum să ne gândim la ce structură de date și ce informații trebuie să salvăm în smart contract.

Voi descrie tot ce păstrăm.

`seqno` este un număr întreg pozitiv pe 32 de biți utilizat ca un contor. 

`pubkey` este un număr întreg pozitiv pe 256 de biți, cheia publică prin care vom verifica semnătura mesajului trimis din exterior, despre care vom discuta mai târziu. 

`order_seqno` este un număr întreg pozitiv pe 32 de biți ce păstrează contorul numărului de oferte. 

`number_of_wins` este un număr întreg pozitiv pe 32 de biți ce păstrează numărul de victorii. 

`incoming_amount` este un tip de date Gram (primele 4 biți reprezintă lungimea), ce păstrează cantitatea totală de grame trimise către contract. 

`outgoing_amount` este cantitatea totală de grame trimisă câștigătorilor. 

`owner_wc` este numărul de workchain, un număr întreg pe 32 de biți (în unele locuri este menționat ca fiind pe 8 biți). În prezent, sunt doar două -1 și 0. 

`owner_account_id` este un număr întreg pozitiv pe 256 de biți, adresa contractului în workchain-ul curent. 

`orders` este o variabilă de tip dicționar ce păstrează ultimele douăzeci de oferte. 

Următorul pas este să scriem două funcții. Prima se va numi pack_state(), care va împacheta datele pentru a fi salvate ulterior în depozitul contractului inteligent. A doua, se va numi unpack_state() , va citi și va returna datele din depozit.

_ pack_state(int seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) inline_ref {
    return begin_cell()
            .store_uint(seqno, 32)
            .store_uint(pubkey, 256)
            .store_uint(order_seqno, 32)
            .store_uint(number_of_wins, 32)
            .store_grams(incoming_amount)
            .store_grams(outgoing_amount)
            .store_int(owner_wc, 32)
            .store_uint(owner_account_id, 256)
            .store_dict(orders)
            .end_cell();
}

_ unpack_state() inline_ref {
    var ds = begin_parse(get_data());
    var unpacked = (ds~load_uint(32), ds~load_uint(256), ds~load_uint(32), ds~load_uint(32), ds~load_grams(), ds~load_grams(), ds~load_int(32), ds~load_uint(256), ds~load_dict());
    ds.end_parse();
    return unpacked;
}

Adăugăm cele două funcții la începutul contractului inteligent. Se va obține un cod intermediar de un rezultat intermediar.

Pentru a salva datele, va trebui să apelăm funcția încorporată set_data() , care va scrie datele din pack_state() în depozitul contractului inteligent.

cell packed_state = pack_state(arg_1, .., arg_n); 
set_data(packed_state);

Acum că avem funcțiile convenabile pentru scrierea și citirea datelor, ne putem continua drumul.

Trebuie să verificăm că mesajul primit din exterior este semnat de proprietarul contractului (sau de un alt utilizator care are acces la cheia privată).

Când publicăm un contract inteligent, putem să-l inițializăm cu datele dorite în stocare, care vor fi păstrate pentru utilizare ulterioară. Vom scrie acolo cheia publică, pentru a putea verifica că semnătura mesajului primit a fost realizată cu cheia privată corespunzătoare.

Înainte de a continua, să creăm o cheie privată și să o scriem în test/keys/owner.pk. Pentru aceasta, vom rula Fift în modul interactiv și vom executa patru comenzi.

`newkeypair` generează o cheie publică și una privată și le scrie în stivă. 

`drop` elimină din stivă elementul de sus (în acest caz, cheia publică)  

`.s` pur și simplu verificăm ce se află în stivă în acel moment 

`"owner.pk" B>file` scrie cheia privată într-un fișier numit `owner.pk`. 

`bye` închide aplicația Fift. 

Vom crea un folder chei în interiorul folderului test și acolo vom scrie cheia privată.

mkdir test/keys
cd test/keys
~ /TON/build/crypto/fift -i 
newkeypair
 ok
.s 
BYTES:128DB222CEB6CF5722021C3F21D4DF391CE6D5F70C874097E28D06FCE9FD6917 BYTES:DD0A81AAF5C07AAAA0C7772BB274E494E93BB0123AA1B29ECE7D42AE45184128 
drop 
 ok
"owner.pk" B>file
 ok
bye

Vedem în folderul curent fișierul owner.pk.

Eliminăm cheia publică din stivă, iar când este necesar, o putem obține din cheia privată.

Acum trebuie să scriem verificarea semnăturii. Să începem cu un test. Mai întâi citim cheia privată din fișier folosind funcția file>B și o scriem în variabila owner_private_key, apoi, cu ajutorul funcției priv>pub convertim cheia privată în publică și scriem rezultatul în owner_public_key.

variable owner_private_key
variable owner_public_key 

"./keys/owner.pk" file>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !

Ambele chei ne vor fi necesare.

Inițializăm stocarea contractului inteligent cu date arbitrare în aceeași secvență ca în funcția pack_state()și scriem în variabila storage.

cheie_privată_variabilă
cheie_publică_variabilă
comenzi_variabilă
owner_wc_variabilă
id_cont_variabilă

"./chei/owner.pk" fișier&gt;B cheie_privată_variabilă !
cheie_privată_variabilă @ priv&gt;pub cheie_publică_variabilă !
dictnew comenzi !
0 owner_wc_variabilă !
0 id_cont_variabilă !

<b 0 32 u, owner_public_key @ b, gram, owner_wc i, owner_account_id 256 orders dict, b> stocare !

Apoi vom compune un mesaj semnat, care va conține doar semnătura și valoarea contorului.

Mai întâi creăm datele pe care dorim să le transmitem, apoi le semnăm cu cheia privată și, în cele din urmă, formăm mesajul semnat.

mesaj variabil de semnat
mesaj variabil de trimis
semnătură variabilă
<b 0 32 u, b> mesaj_de_semnat !
mesaj_de_semnat @ cheie_privată_hashi @ ed25519_semn_uint semnătură !
<b signature @ b, 0 32 u, b> &lt;s !  

În rezultat, mesajul pe care îl vom trimite în contractul inteligent este scris în variabila message_to_send, despre funcțiile hashu, ed25519_sign_uint puteți citi în documentația Fift.

Și pentru a reporni testul, apelăm din nou.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Așa ar trebui să arate fișierul cu testele în această etapă.

Să rulăm testul și va cădea, așa că vom modifica contractul inteligent pentru a putea primi mesaje de acest format și a verifica semnătura.

În primul rând, citim din mesaj 512 biți ai semnăturii și îi salvăm într-o variabilă, apoi citim 32 de biți din variabila contor.

Având în vedere că avem o funcție de citire a datelor din depozitul contractului inteligent, o vom folosi.

Apoi verificăm contorul transmis cu depozitul și verificăm semnătura. Dacă ceva nu se potrivește, aruncăm o excepție cu codul corespunzător.

var signature = in_msg~load_bits(512);
var message = in_msg;
int msg_seqno = message~load_uint(32);
(int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(in_msg), signature, pubkey));

Commit corespunzător aici.

Să rulăm testele și vom vedea că al doilea test eșuează. Din două motive, lipsa biților în mesaj și lipsa biților în depozit, astfel că codul eșuează în timpul analizei. Trebuie să adăugăm semnătura mesajului pe care îl trimitem și să copiem depozitul din ultimul test.

În al doilea test, vom adăuga semnătura mesajului și vom modifica depozitul contractului inteligent. Așa ar trebui așa arată fișierul cu testele în acest moment.

Vom scrie un al patrulea test, în care vom trimite un mesaj semnat cu cheia privată a unei alte persoane. Vom crea o altă cheie privată și o vom salva într-un fișier. not-owner.pk. Cu această cheie privată vom semna mesajul. Să rulăm testele și să ne asigurăm că toate testele trec. Commit în prezent.

Acum, în sfârșit, putem trece la implementarea logicii contractului inteligent.
În recv_external() vom accepta două tipuri de mesaje.

Având în vedere că contractul nostru va acumula pierderile jucătorilor, acești bani trebuie transferați creatorului loteriei. Adresa portofelului creatorului loteriei este salvată în depozit atunci când contractul este creat.

Pentru siguranță, avem nevoie de capacitatea de a schimba adresa la care trimitem grame pierdute. De asemenea, trebuie să avem posibilitatea de a trimite grame din loterie către adresa proprietarului.

Să începem cu primul. Vom scrie mai întâi un test care va verifica dacă, după trimiterea mesajului, contractul inteligent a salvat noua adresă în depozit. Să observăm că în mesaj, pe lângă contor și noua adresă, mai transmitem action un număr întreg neagativ de 7 biți, în funcție de acesta, vom alege cum să tratăm mesajul în contractul inteligent.

<b 0 32 u, 1 @ 7 new_owner_wc i, new_owner_account_id 256 b> mesaj_de_semnat !

În test, se poate vedea cum se desfășoară deserializarea depozitului contractului inteligent. storage În Fift. Deserializarea variabilelor este descrisă în documentația Fift.

Link către commit cu adăugarea unui test.

Executăm testul și ne asigurăm că acesta eșuează. Acum vom adăuga logica de schimbare a adresei proprietarului loteriei.

În smart contract continuăm să analizăm message, citim în action. Să ne amintim că vom avea două action: schimbarea adresei și trimiterea gramilor.

Apoi citim noua adresă a proprietarului contractului și o salvăm în depozit.
Executăm testele și vedem că al treilea test eșuează. Eșuează pentru că acum contractul analizează suplimentar 7 biți din mesaj, care lipsesc în test. Să adăugăm în mesaj un action. Vom rula testele și vedem că toate trec. Aici commit pentru modificări. Excelent.

Acum să scriem logica de trimitere a cantității specificate de grame la adresa salvată anterior.

Mai întâi să scriem un test. Vom scrie două teste: unul când nu sunt suficiente fonduri, altul când totul ar trebui să treacă cu succes. Testele pot fi vizualizate în acest commit.

Acum să completăm codul. Mai întâi vom scrie două metode auxiliare. Prima este metoda get pentru a afla soldul curent al smart contractului.

int balance() inline_ref method_id {
    return get_balance().pair_first();
}

Și a doua pentru trimiterea gramilor către un alt smart contract. Această metodă am copiat-o complet dintr-un alt smart contract.

() send_grams(int wc, int addr, int grams) impure {
    ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
    cell msg = begin_cell()
    ;;  .store_uint(0, 1) ;; 0 <= indicator format int_msg_info$0 
    ;;  .store_uint(1, 1) ;; 1 <= ihr dezactivat
    ;;  .store_uint(1, 1) ;; 1 <= bounce = adevărat
    ;;  .store_uint(0, 1) ;; 0 <= bounced = fals
    ;;  .store_uint(4, 5)  ;; 00100 <= steaguri adresă, anycast = fals, 8-bit workchain
        .store_uint (196, 9)
        .store_int(wc, 8)
        .store_uint(addr, 256)
        .store_grams(grams)
        .store_uint(0, 107); ;; 106 zero-uri +  0 ca indicator că nu există o celulă cu datele.
        .end_cell(); 
    send_raw_message(msg, 3); ;; modul, 2 pentru ignorarea erorilor, 1 pentru plățile taxelor de către expeditor, 64 pentru returnarea valorii mesajului inbound
}

Vom adăuga aceste două metode în smart contract și vom scrie logica. Mai întâi analizăm cantitatea de grame din mesaj. Apoi verificăm soldul, dacă nu sunt suficiente, aruncăm o excepție. Dacă totul este în regulă, atunci trimitem gramii către adresa salvată și actualizăm contorul.

int amount_to_send = message~load_grams();
throw_if(36, amount_to_send + 500000000 > balance());
accept_message();
send_grams(owner_wc, owner_account_id, amount_to_send);
set_data(pack_state(stored_seqno + 1, pubkey, order_seqno, number_of_wins, incoming_amount, outgoing_amount, owner_wc, owner_account_id, orders));

Așa ar trebui arată smart contractul în acest moment. Vom rula testele și ne vom asigura că trec.

Apropo, pentru fiecare mesaj procesat, un comision este retras de la contractul inteligent. Pentru ca contractul inteligent să efectueze cererea, după verificările de bază, trebuie să apelăm accept_message().

Acum să ne ocupăm de mesajele interne. De fapt, vom accepta doar grame și le vom trimite jucătorului înapoi suma dublă la câștig și o treime proprietarului la pierdere.

Întâi, să scriem un test simplu. Pentru asta, ne va trebui o adresă de test a contractului inteligent de pe care să trimitem grame către contractul inteligent.

Adresa contractului inteligent constă din două numere: un număr întreg de 32 de biți care răspunde pentru workchain și un număr unic de 256 de biți ne-negativ pentru contul din acest workchain. De exemplu, -1 și 12345, această adresă o vom salva în fișier.

Am copiat funcția de salvare a adresei din TonUtil.fif.

// ( wc addr fname -- )  Save address to file in 36-byte format
{ -rot 256 u>B swap 32 i>B B+ swap B>file } : save-address

Să analizăm cum funcționează funcția, aceasta va oferi o înțelegere despre cum funcționează Fift. Pornim Fift în modul interactiv.

~\/TON\/build\/crypto\/fift -i 

Întâi punem în stiva -1, 12345 și numele viitorului fișier "sender.addr":

-1 12345 "sender.addr" 

Pasul următor execută funcția -rot, care mută stiva, astfel încât numărul unic al contractului inteligent să ajungă deasupra stivei:

"sender.addr" -1 12345

256 u>B convertește un întreg de 256 de biți ne-negativ în byte.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap interschimbă cele două elemente de sus ale stivei.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B convertește un întreg de 32 de biți în byte.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ concatenatează două secvențe de byte.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Din nou swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Și în cele din urmă, executăm scrierea byte-urilor în fișier B>file. După aceasta, stiva noastră este goală. Oprim Fift. În folderul curent a fost creat un fișier sender.addr. Vom muta fișierul în folderul creat test\/addresses\/.

Să scriem un test simplu care va trimite grame către contractul inteligent. Iată commit-ul.

Acum să ne ocupăm de logica loteriei.

Primul lucru pe care-l facem este să verificăm mesajul bounced sau nu, dacă bounced, atunci îl ignorăm. bounced asta înseamnă că contractul va returna grame dacă survine o eroare. Nu vom returna grame în cazul unei erori neprevăzute.

Verificăm, soldul, dacă este mai mic de jumătate de gram, atunci pur și simplu acceptăm mesajul și-l ignorăm.

Apoi, parsăm adresa smart contract-ului de la care a venit mesajul.

Citim datele din depozit și apoi eliminăm vechile pariuri din istorie dacă sunt mai mult de douăzeci. Pentru confort, am scris trei funcții suplimentare. pack_order(), unpack_order(), remove_old_orders().

Apoi, ne uităm dacă nu este suficient sold pentru plată, atunci considerăm că aceasta nu este o pariu, ci o replenizare și salvăm replenizarea în orders.

Apoi, în sfârșit, esența smart contract-ului.

Mai întâi, dacă jucătorul pierde, îl salvăm în istoria pariurilor și dacă suma este mai mare de 3 grame, trimitem 1/3 proprietarului smart contract-ului.

Dacă jucătorul câștigă, trimitem suma dublată la adresa jucătorului și apoi salvăm informația despre pariu în istorie.

() recv_internal(int order_amount, cell in_msg_cell, slice in_msg) impure {
    var cs = in_msg_cell.begin_parse();
    int flags = cs~load_uint(4);  ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
    if (flags & 1) { ;; ignore bounced
        return ();
    }
    if (order_amount < 500000000) { ;; just receive grams without changing state 
        return ();
    }
    slice src_addr_slice = cs~load_msg_addr();
    (int src_wc, int src_addr) = parse_std_addr(src_addr_slice);
    (int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
    orders = remove_old_orders(orders, order_seqno);
    if (balance() = 4) {
        builder order = pack_order(order_seqno, 3, now(), order_amount, src_wc, src_addr);
        orders~udict_set_builder(32, order_seqno, order);
        set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins, incoming_amount + order_amount, outgoing_amount, owner_wc, owner_account_id, orders));
        if (order_amount > 3000000000) {
            send_grams(owner_wc, owner_account_id, order_amount / 3);
        }
        return ();
    }
    send_grams(src_wc, src_addr, 2 * order_amount);
    builder order = pack_order(order_seqno, 2, now(), order_amount, src_wc, src_addr);
    orders~udict_set_builder(32, order_seqno, order);
    set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins + 1, incoming_amount, outgoing_amount + 2 * order_amount, owner_wc, owner_account_id, orders));
}

Și asta e tot. Commitul corespunzător.

Acum rămâne simplu, vom face metode de tip get, astfel încât din lumea externă să putem obține informații despre starea contractului (de fapt să citim datele din depozitul smart contract-ului).

Să adăugăm metode de tip get. Vom scrie mai jos despre cum să obținem informații despre smart contract.

Am uitat să adaug codul care va gestiona prima cerere care are loc la publicarea smart contractului. Commitul corespunzător. Și încă am corectat bug-ul cu trimiterea a 1/3 din sumă pe contul proprietarului.

Apoi rămâne să publicăm smart contractul. Vom crea un folder requests..

Am folosit ca bază codul de publicare simple-wallet-code.fc care , se pot găsi se află în depozitul oficial.

Din ce merită să ne concentrăm. Formăm stocarea smart contractului și mesajul de intrare. După aceasta, se generează adresa smart contractului, adică adresa este cunoscută încă înainte de publicarea în TON. Apoi, la această adresă trebuie să trimitem câțiva grami și abia după aceea trebuie să trimitem fișierul cu smart contractul în sine, deoarece rețeaua percepe o comision pentru stocarea smart contractului și operațiunile din acesta (validatorii care stochează și execută smart contractele). Codul poate fi vizualizat aici.

Apoi, executăm codul de publicare și obținem lottery-query.boc fișierul și adresa smart contractului.

~\/TON\/build\/crypto\/fift -s requests\/new-lottery.fif 0

Nu uitați să salvați fișierele generate: lottery-query.boc, lottery.addr, lottery.pk.

Printre altele, în logurile de execuție vom vedea adresa smart contractului.

new wallet address = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a 
(Se salvează adresa în fișierul lottery.addr)
Adresa non-bouncable (pentru inițiere): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Adresa bouncable (pentru acces ulterior): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFY

De curiozitate, să facem o cerere în TON

$ .\/lite-client\/lite-client -C ton-lite-client-test1.config.json 
getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Și vom vedea că contul cu această adresă este gol.

starea contului este goală

Trimitem pe adresa 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram și după câteva secunde executăm aceeași comandă. Pentru a trimite grami folosesc portofelul oficial, iar gramele de test pot fi cerute de la cineva din chat despre care voi vorbi la sfârșitul articolului.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Se observă că în rețea a apărut un smart contract neinițializat (state:account_uninit) cu o adresă și un sold de 1 000 000 000 nanograme.

starea contului este (cont
  addr:(addr_std
    anycast:nimic workchain_id:0 adresa:x044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A)
  storage_stat:(storage_info
    used:(storage_used
      cells:(var_uint len:1 value:1)
      bits:(var_uint len:1 value:103)
      public_cells:(var_uint len:0 value:0)) ultima_plata:1583257959
    plata_datorie:nimic)
  storage:(account_storage last_trans_lt:3825478000002
    balans:(currencies
      grame:(nanograms
        amount:(var_uint len:4 value:2000000000))
      altele:(extra_currencies
        dict:hme_empty))
    stare:account_uninit))
x{C00044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A20259C2F2F4CB3800000DEAC10776091DCD650004_}
ultima tranzactie lt = 3825478000001 hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
balansul contului este 2000000000ng

Acum vom publica contractul smart. Vom lansa lite-client și vom executa.

> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] trimitere interogare din fișier lottery-query.boc
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query] statusul mesajului extern este 1 

Vom verifica dacă contractul a fost publicat.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Printre altele, vom obține.

  storage:(account_storage last_trans_lt:3825499000002
    balans:(currencies
      grame:(nanograms
        amount:(var_uint len:4 value:1987150999))
      altele:(extra_currencies
        dict:hme_empty))
    stare:(account_active

Observăm că account_active.

Commitul corespunzător cu modificări aici.

Acum vom crea solicitări pentru interacțiunea cu contractul smart.

Mai precis, prima pentru schimbarea adresei o vom lăsa ca lucrare independentă, iar a doua pentru trimiterea gramelor către adresa proprietarului o vom realiza. De fapt, trebuie să facem același lucru ca în testul de trimitere a gramelor.

Iată ce mesaj vom trimite către contractul smart, unde msg_seqno 165, action 2 și 9.5 grame pentru trimitere.

<b 165 32 u, 2 7 9500000000 gram, b>

Nu uitați să semnați mesajul cu cheia privată lottery.pk, care a fost generată anterior la crearea contractului smart. Iată commitul corespunzător.

Obținem informația din contractul smart folosind metodele get

Acum vom examina cum să lansăm metodele get din contractul smart.

Lansăm lite-client și lansăm metodele get pe care le-am scris.

$ .\/lite-client\/lite-client -C ton-lite-client-test1.config.json
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd balance
argumente:  [ 104128 ] 
rezultatul:  [ 64633878952 ] 
...

În rezultat conține valoarea pe care o returnează funcția balance() din contractul nostru smart.
Același lucru îl vom executa și pentru câteva metode.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
argumente:  [ 77871 ] 
rezultatul:  [ 1 ] 

Vom solicita istoricul ofertelor.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_orders
...
argumente:  [ 67442 ] 
rezultatul:  [ ([0 1 1583258284 10000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [1 3 1583258347 4000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [2 1 1583259901 50000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308]) ] 

Vom folosi lite-client și metodele GET pentru a afișa informații despre contractele inteligente pe site.

Afișăm datele contractului inteligent pe site

Am scris un site web simplu pe Python pentru a prezenta date din contractul inteligent într-un mod convenabil. Aici nu voi detalia, ci voi publica site-ul cu un singur commit.

Cereri către TON sunt făcute din Python folosind lite-client. Pentru comoditate, site-ul este ambalat în Docker și publicat pe Google Cloud. Link către site.

Să încercăm

Acum vom încerca să trimitem grame pentru alimentare din portofel. Vom trimite 40 grame. Și vom face câteva pariuri pentru ilustrare. Observăm că site-ul afișează istoricul pariurilor, procentajul câștigat curent și alte informații utile.

Observăm, că prima a fost câștigată, a doua a fost pierdută.

Cuvânt înainte

Articolul a devenit mult mai lung decât mă așteptam, poate ar fi putut fi mai scurt, sau poate este tocmai ceea ce îi trebuie unei persoane care nu știe nimic despre TON și vrea să scrie și să publice un contract inteligent nu foarte simplu cu posibilitatea de interacțiune. Poate că unele lucruri ar fi putut fi explicate mai simplu.

Poate că unele aspecte din implementare ar fi putut fi realizate mai eficient și elegant, dar atunci ar fi durat mult mai mult să pregătesc articolul. De asemenea, este posibil să fi greșit sau să nu fi înțeles ceva, așa că dacă faceți ceva serios, este bine să vă bazați pe documentația oficială sau pe repository-ul oficial cu codul TON.

Trebuie menționat că, având în vedere că TON este încă în faza activă de dezvoltare, pot apărea modificări care să strice vreunul dintre pașii din acest articol (ceea ce a și fost, am corectat deja), dar abordarea generală nu se va schimba probabil.

Nu voi specula despre viitorul TON. Este posibil ca platforma să devină ceva mai mare și merită să investim timp în studierea ei și să ocupăm o nișă cu produsele noastre chiar acum.

Există și Libra de la Facebook, care are o audiență potențială de utilizatori mai mare decât TON. Nu știu aproape nimic despre Libra, dar judecând după forum, activitatea este mult mai mare decât în comunitatea TON. Deși dezvoltatorii și comunitatea TON par mai degrabă underground, ceea ce este, de asemenea, fantastic.

Linkuri

  1. Documentația oficială pentru TON: https://test.ton.org
  2. Repository-ul oficial TON: https://github.com/ton-blockchain/ton
  3. Portofelul oficial pentru diferite platforme: https://wallet.ton.org
  4. Repository-ul contractului inteligent din acest articol: https://github.com/raiym/astonished
  5. Link către site-ul contractului inteligent: https://ton-lottery.appspot.com
  6. Repository pentru extensia pentru Visual Studio Code pentru FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Un chat despre TON în Telegram, care m-a ajutat foarte mult să mă familiarizez la început. Cred că nu greșesc când spun că acolo sunt toți cei care au scris ceva pentru TON. De asemenea, poți cere gramuri de testare. https://t.me/tondev_ru
  8. Încă un chat despre TON în care am găsit informații utile: https://t.me/TONgramDev
  9. Prima etapă a concursului: https://contest.com/blockchain
  10. A doua etapă a concursului: https://contest.com/blockchain-2

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster