Despre cum să scrieți și să publicați un contract inteligent în Telegram Open Network (TON)

Despre cum să scrieți și să publicați un contract inteligent în TON

Despre ce este acest articol?

În articol voi vorbi despre modul în care am participat la primul (din două) competiții de blocuri Telegram, nu am luat un premiu și am decis să-mi înregistrez experiența într-un articol, astfel încât să nu se scufunde în uitare și, poate, să ajut. cineva.

Deoarece nu am vrut să scriu cod abstract, ci să fac ceva care să funcționeze, pentru articol am scris un contract inteligent pentru o loterie instant și un site web care arată datele contractului inteligent direct de la TON fără a utiliza stocarea intermediară.

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

Folosind loteria ca exemplu, voi trece de la instalarea mediului la publicarea unui contract inteligent, interacțiunea cu acesta și scrierea unui site web pentru primirea și publicarea datelor.

Despre participarea la concurs

În octombrie anul trecut, Telegram a anunțat o competiție blockchain cu noi limbi Fift и FunC. A fost necesar să alegeți să scrieți oricare dintre cele cinci contracte inteligente propuse. M-am gândit că ar fi frumos să fac ceva diferit, să învăț o limbă și să fac ceva, chiar dacă nu trebuie să scriu altceva pe viitor. În plus, subiectul este în permanență pe buze.

Merită spus că nu am avut experiență în dezvoltarea de contracte inteligente.

Mi-am propus să particip până la sfârșit până am putut și apoi să scriu un articol de recenzie, dar am eșuat imediat la primul. eu a scris un portofel cu multi-semnătură activată FunC și în general a funcționat. L-am luat ca bază contract inteligent pe Solidity.

În acel moment, am crezut că acest lucru era cu siguranță suficient pentru a lua măcar un loc de premiu. Ca urmare, aproximativ 40 din 60 de participanți au devenit câștigători de premii și eu nu m-am numărat printre ei. În general, nu este nimic în neregulă cu asta, dar un lucru m-a deranjat. La momentul anunțării rezultatelor, revizuirea testului pentru contractul meu nu se făcuse, i-am întrebat pe participanții la chat dacă mai este cineva care nu îl avea, nu era niciunul.

Aparent acordând atenție mesajelor mele, două zile mai târziu judecătorii au publicat un comentariu și încă nu înțeleg dacă mi-au ratat din greșeală contractul inteligent în timpul jurizării sau pur și simplu s-au gândit că a fost atât de rău încât nu a avut nevoie de un comentariu. Am pus o întrebare pe pagină, dar nu am primit răspuns. Deși nu este un secret cine a judecat, am considerat că nu este necesar să scriu mesaje personale.

S-a cheltuit mult timp pentru înțelegere, așa că s-a decis să se scrie un articol. Deoarece nu există încă o mulțime de informații, acest articol va ajuta să economisiți timp pentru toți cei interesați.

Conceptul de contracte inteligente în TON

Înainte de a scrie ceva, trebuie să-ți dai seama de ce parte să abordezi acest lucru. Prin urmare, acum vă voi spune din ce părți este compus sistemul. Mai exact, ce părți trebuie să știi pentru a scrie măcar un fel de contract de muncă.

Ne vom concentra pe scrierea unui contract inteligent și pe lucrul cu el TON Virtual Machine (TVM), Fift и FunC, deci articolul este mai mult ca o descriere a dezvoltării unui program obișnuit. Nu ne vom opri asupra modului în care funcționează platforma în sine.

În general, despre cum funcționează TVM și limbajul Fift există documentație oficială bună. În timp ce participam la concurs și acum când scriam actualul contract, am apelat adesea la ea.

Principala limbă în care sunt scrise contractele inteligente este FunC. Nu există nicio documentație despre el în acest moment, așa că pentru a scrie ceva trebuie să studiați exemple de contracte inteligente din depozitul oficial și implementarea limbajului în sine acolo, plus că puteți privi exemple de contracte inteligente din ultimele două concursuri. Link-uri la finalul articolului.

Să presupunem că am scris deja un smart contract pentru FunC, după aceea compilam codul în asamblatorul Fift.

Contractul inteligent compilat rămâne să fie publicat. Pentru a face acest lucru, trebuie să scrieți o funcție în Fift, care va lua codul de contract inteligent și alți parametri ca intrare, iar rezultatul va fi un fișier cu extensia .boc (care înseamnă „pungă de celule”) și, în funcție de modul în care o scriem, o cheie privată și o adresă, care este generată pe baza codului de contract inteligent. Puteți trimite deja grame la adresa unui contract inteligent care nu a fost încă publicat.

Pentru a publica un contract inteligent în TON primit .boc fișierul va trebui trimis către blockchain folosind un client ușor (mai multe despre asta mai jos). Dar înainte de publicare, trebuie să transferați grame la adresa generată, altfel contractul inteligent nu va fi publicat. După publicare, puteți interacționa cu contractul inteligent trimițându-i mesaje din exterior (de exemplu, folosind un client light) sau din interior (de exemplu, un contract inteligent trimite altuia un mesaj în interiorul TON).

Odată ce înțelegem cum este publicat codul, devine mai ușor. Știm aproximativ ce vrem să scriem și cum va funcționa programul nostru. Și în timp ce scriem, căutăm cum este deja implementat acest lucru în contractele inteligente existente sau ne uităm la codul de implementare Fift и FunC în depozitul oficial sau căutați în documentația oficială.

De foarte multe ori am căutat cuvinte cheie în chat-ul Telegram unde se adunau toți participanții la concurs și angajații Telegram și s-a întâmplat că în timpul competiției toată lumea s-a adunat acolo și a început să discute despre Fift și FunC. Link la finalul articolului.

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

Pregătirea mediului pentru lucrul cu TON

Am făcut tot ce va fi descris în articolul despre MacOS și l-am verificat de două ori în Ubuntu 18.04 LTS curat pe Docker.

Primul lucru pe care trebuie să-l faceți este să descărcați și să instalați lite-client cu care puteți trimite cereri către TON.

Instrucțiunile de pe site-ul oficial descriu procesul de instalare destul de detaliat și clar și omit câteva detalii. Aici urmăm instrucțiunile, instalând dependențele lipsă pe parcurs. Nu am compilat singur fiecare proiect și l-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 

Odată ce toate dependențele sunt instalate, puteți instala lite-client, Fift, FunC.

În primul rând, clonăm depozitul TON împreună cu dependențele sale. Pentru comoditate, vom face totul într-un folder ~/TON.

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

Depozitul stochează și implementări Fift и FunC.

Acum suntem gata să asamblam proiectul. Codul de depozit este clonat într-un folder ~/TON/ton. În ~/TON creați un folder build și colectați proiectul în el.

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

Deoarece vom scrie un contract inteligent, nu avem nevoie doar lite-clientDar Fift с FunC, deci hai să compilam totul. Nu este un proces rapid, așa că așteptăm.

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

Apoi, descărcați fișierul de configurare care conține date despre nodul la care lite-client se va conecta.

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

Efectuarea primelor solicitări către TON

Acum hai să lansăm lite-client.

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

Dacă construirea a avut succes, după lansare veți vedea un jurnal al conexiunii clientului ușor la nod.

[ 1][t 2][1582054822.963129282][lite-client.h:201][!testnode]   conn ready
[ 2][t 2][1582054823.085654020][lite-client.cpp:277][!testnode] server version is 1.1, capabilities 7
[ 3][t 2][1582054823.085725069][lite-client.cpp:286][!testnode] server time is 1582054823 (delta 0)
...

Puteți rula comanda help și vedeți ce comenzi sunt disponibile.

help

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

list of available commands:
last    Get last block and state info from server
sendfile <filename> Load a serialized message from <filename> and send it to server
getaccount <addr> [<block-id-ext>]  Loads the most recent state of specified account; <addr> is in [<workchain>:]<hex-or-base64-addr> format
runmethod <addr> [<block-id-ext>] <method-id> <params>...   Runs GET method <method-id> of account <addr> with specified parameters

last получает последний созданный блок с сервера. 

sendfile <filename> отправляет в TON файл с сообщением, именно с помощью этой команды публикуется смарт-контракт и запрсосы к нему. 

getaccount <addr> загружает текущее состояние смарт-контракта с указанным адресом. 

runmethod <addr> [<block-id-ext>] <method-id> <params>  запускает get-методы смартконтракта. 

Acum suntem gata să scriem contractul în sine.

punerea în aplicare

Idee

După cum am scris mai sus, contractul inteligent pe care îl scriem este o loterie.

Mai mult, aceasta nu este o loterie la care trebuie să cumperi un bilet și să aștepți o oră, zi sau lună, ci una instantanee în care utilizatorul se transferă la adresa contractului N grame și îl recuperează instantaneu 2 * N grame sau pierde. Vom face ca probabilitatea de a câștiga aproximativ 40%. Dacă nu sunt suficiente grame pentru plată, atunci vom considera tranzacția ca o reîncărcare.

Mai mult, este important ca pariurile să poată fi văzute în timp real și într-o formă convenabilă, astfel încât utilizatorul să poată înțelege imediat dacă a câștigat sau a pierdut. Prin urmare, trebuie să faci un site web care să arate pariuri și rezultate direct de la TON.

Scrierea unui contract inteligent

Pentru comoditate, am evidențiat codul pentru FunC; pluginul poate fi găsit și instalat în căutarea Visual Studio Code; dacă dintr-o dată doriți să adăugați ceva, am pus pluginul disponibil public. De asemenea, cineva a făcut anterior un plugin pentru a lucra cu Fift, îl puteți instala și îl găsiți în VSC.

Să creăm imediat un depozit în care vom trimite rezultatele intermediare.

Pentru a ne ușura viața, vom scrie un contract inteligent și îl vom testa la nivel local până când este gata. Abia după aceea o vom publica în TON.

Contractul inteligent are două metode externe care pot fi accesate. Primul, recv_external() această funcție este executată atunci când o solicitare către contract vine din lumea exterioară, adică nu de la TON, de exemplu, când noi înșine generăm un mesaj și îl trimitem prin lite-client. Al doilea, recv_internal() atunci, în cadrul TON însuși, orice contract se referă la al nostru. În ambele cazuri, puteți transmite parametrii funcției.

Să începem cu un exemplu simplu care va funcționa dacă este publicat, dar nu există încărcare funcțională în el.

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

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

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

TVM cell slice sau slice aceasta face parte din cea existentă cell este folosit pentru a o analiza, va deveni clar mai târziu. Principalul lucru pentru noi este că ne putem transfera slice și, în funcție de tipul de mesaj, procesați datele în recv_external() sau recv_internal().

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

Să salvăm codul contractului în lottery-code.fc și compilați.

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

Semnificația steagurilor poate fi vizualizată folosind comanda

~/TON/build/crypto/func -help

Am compilat codul de asamblare Fift î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

Poate fi lansat local, pentru asta vom pregăti mediul.

Rețineți că prima linie se conectează Asm.fif, acesta este codul scris în Fift pentru asamblatorul Fift.

Deoarece dorim să rulăm și să testăm contractul inteligent la nivel local, vom crea un fișier lottery-test-suite.fif și copiați codul compilat acolo, înlocuind ultima linie din el, care scrie codul smart contract într-o constantă codepentru a-l transfera apoi pe 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 să adăugăm la același fișier codul pe care îl vom folosi pentru a lansa 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()

В c7 înregistrăm contextul, adică datele cu care va fi lansat TVM-ul (sau starea rețelei). Chiar și în timpul competiției, unul dintre dezvoltatori a arătat cum să creeze c7 si am copiat. În acest articol este posibil să fie nevoie să ne schimbăm rand_seed deoarece generarea unui număr aleatoriu depinde de acesta și dacă nu este schimbat, același număr va fi returnat de fiecare dată.

recv_internal и recv_external constantele cu valorile 0 și -1 vor fi responsabile pentru apelarea funcțiilor corespunzătoare din contractul inteligent.

Acum suntem gata să creăm primul test pentru contractul nostru inteligent gol. Pentru claritate, deocamdată vom adăuga toate testele la același fișier lottery-test-suite.fif.

Să creăm o variabilă storage și scrieți unul gol în el cell, aceasta va fi stocarea smart contract.

message Acesta este mesajul pe care îl vom transmite contactului inteligent din exterior. Îl vom goli și pentru moment.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

După ce am pregătit constantele și variabilele, lansăm TVM folosind comanda runvmctx și transmiteți parametrii creați la intrare.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Până la urmă vom reuși asa cod intermediar pentru Fift.

Acum putem rula codul rezultat.

export FIFTPATH=~/TON/ton/crypto/fift/lib // выполняем один раз для удобства 
~/TON/build/crypto/fift -s lottery-test-suite.fif 

Programul ar trebui să ruleze fără erori și în rezultat vom vedea jurnalul de execuție:

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

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

Acum trebuie să adăugăm funcționalitate. Mai întâi să ne ocupăm de mesajele care vin din lumea exterioară către recv_external()

Dezvoltatorul însuși alege formatul de mesaj pe care contractul îl poate accepta.

Dar de obicei

  • în primul rând, dorim să ne protejăm contractul de lumea exterioară și să îl facem astfel încât numai proprietarul contractului să îi poată trimite mesaje externe.
  • în al doilea rând, atunci când trimitem un mesaj valid către TON, dorim ca acest lucru să se întâmple exact o dată și când trimitem din nou același mesaj, contractul inteligent îl respinge.

Așa că aproape fiecare contract rezolvă aceste două probleme, deoarece contractul nostru acceptă mesaje externe, trebuie să ne ocupăm și de asta.

O vom face în ordine inversă. În primul rând, să rezolvăm problema prin repetare; dacă contractul a primit deja un astfel de mesaj și l-a procesat, nu îl va executa a doua oară. Și apoi vom rezolva problema astfel încât doar un anumit cerc de oameni să poată trimite mesaje către contractul inteligent.

Există diferite moduri de a rezolva problema cu mesajele duplicate. Iată cum o vom face. În contractul inteligent, inițializam contorul de mesaje primite cu valoarea inițială 0. În fiecare mesaj la contractul inteligent, vom adăuga valoarea curentă a contorului. Dacă valoarea contorului din mesaj nu se potrivește cu valoarea din contractul inteligent, atunci nu o procesăm; dacă o face, atunci o procesăm și creștem contorul din contractul inteligent cu 1.

Să revenim la lottery-test-suite.fif și adăugați un al doilea test la acesta. Dacă trimitem un număr incorect, codul ar trebui să arunce o excepție. De exemplu, lăsați datele contractului să stocheze 166, iar noi vom trimite 165.

<b 166 32 u, b> storage !
<b 165 32 u, b> message !

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx

drop 
exit_code ! 
."Exit code " exit_code @ . cr 
exit_code @ 33 - abort"Test #2 Not passed"

Hai să lansăm.

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

Și vom vedea că testul este executat cu o eroare.

[ 1][t 0][1582283084.210902214][words.cpp:3046] lottery-test-suite.fif:67: abort": Test #2 Not passed
[ 1][t 0][1582283084.210941076][fift-main.cpp:196]      Error interpreting file `lottery-test-suite.fif`: error interpreting included file `lottery-test-suite.fif` : lottery-test-suite.fif:67: abort": Test #2 Not passed

În această etapă, lottery-test-suite.fif ar trebui să arate ca по ссылке.

Acum să adăugăm logica contorului la contractul inteligent în lottery-code.fc.

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

() 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);
}

В slice in_msg este mesajul pe care îl transmitem.

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

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

În continuare, încărcăm 32 de biți din stocarea smart contract. Verificăm dacă numărul încărcat se potrivește cu cel trecut; dacă nu, aruncăm o excepție. În cazul nostru, din moment ce trecem un non-meci, ar trebui aruncată o excepție.

Acum să compilam.

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

Copiați codul rezultat în lottery-test-suite.fif, fără a uita să înlocuim ultima linie.

Verificăm dacă testul trece:

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

Chiar aici Puteți vedea comiterea corespunzătoare cu rezultatele curente.

Rețineți că este incomod să copiați în mod constant codul compilat al unui contract inteligent într-un fișier cu teste, așa că haideți să scriem un script care va scrie codul într-o constantă pentru noi și pur și simplu vom conecta codul compilat la testele noastre folosind "include".

Creați un fișier în folderul proiectului 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

Să-l facem executabil.

chmod +x ./build.sh

Acum, rulați scriptul nostru pentru a compila contractul. Dar, pe lângă aceasta, trebuie să o scriem într-o constantă code. Deci vom crea un fișier nou lotter-compiled-for-test.fif, pe care o vom include în dosar lottery-test-suite.fif.

Să adăugăm cod skirpt la sh, care va duplica pur și simplu fișierul compilat în lotter-compiled-for-test.fif și schimbați ultima linie din ea.

# 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 va fi generat un fișier lottery-compiled-for-test.fif, pe care îl vom include în documentul nostru lottery-test-suite.fif

В lottery-test-suite.fif ștergeți codul contractului și adăugați linia "lottery-compiled-for-test.fif" include.

Facem teste pentru a verifica dacă trec.

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

Grozav, acum pentru a automatiza lansarea testelor, haideți să creăm un fișier test.sh, care se va executa mai întâi build.sh, apoi rulați testele.

touch test.sh
chmod +x test.sh

Scriem înăuntru

./build.sh 

echo "nCompilation completedn"

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

Va face test.sh și rulați-l pentru a vă asigura că testele funcționează.

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

Verificăm dacă contractul se întocmește și testele sunt executate.

Grozav, acum la pornire test.sh Testele vor fi compilate și rulate imediat. Aici este linkul către comite.

Bine, înainte de a continua, hai să mai facem un lucru pentru comoditate.

Să creăm un folder build unde vom stoca contractul copiat și clona acestuia scrisă într-o constantă lottery-compiled.fif, lottery-compiled-for-test.fif. Să creăm și un folder test unde va fi stocat fișierul de testare? lottery-test-suite.fif și eventual alte fișiere suport. Link către modificările relevante.

Să continuăm dezvoltarea contractului inteligent.

În continuare ar trebui să existe un test care verifică dacă mesajul este primit și contorul este actualizat în magazin atunci când trimitem numărul corect. Dar asta vom face mai târziu.

Acum să ne gândim la ce structură de date și ce date trebuie stocate în contractul inteligent.

Voi descrie tot ce stocăm.

`seqno` 32-х битное целое положительное число счетчик. 

`pubkey` 256-ти битное целое положительное число публичный ключ, с помощью которого, мы будем проверять подпись отправленного извне сообщения, о чем ниже. 

`order_seqno` 32-х битное целое положительное число хранит счетчик количества ставок. 

`number_of_wins` 32-х битное целое положительное число хранит  количество побед. 

`incoming_amount` тип данных Gram (первые 4 бита отвечает за длину), хранит общее количество грамов, которые были отправлены на контртакт. 

`outgoing_amount` общее количество грамов, которое было отправлено победителям. 

`owner_wc` номер воркчейна, 32-х битное (в некоторых местах написано, что 8-ми битное) целое число. В данный момент всего два -1 и 0. 

`owner_account_id` 256-ти битное целое положительное число, адрес контракта в текущем воркчейне. 

`orders` переменная типа словарь, хранит последние двадцать ставок. 

Apoi trebuie să scrieți două funcții. Să-l sunăm pe primul pack_state(), care va împacheta datele pentru salvarea ulterioară în stocarea smart contract. Să-l sunăm pe al doilea unpack_state() va citi și returna datele din stocare.

_ 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 aceste două funcții la începutul contractului inteligent. Se va rezolva asa rezultat intermediar.

Pentru a salva datele, va trebui să apelați funcția încorporată set_data() și va scrie date din pack_state() în stocarea smart contract.

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

Acum că avem funcții convenabile pentru scrierea și citirea datelor, putem merge mai departe.

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

Când publicăm un contract inteligent, îl putem inițializa cu datele de care avem nevoie în stocare, care vor fi salvate pentru utilizare ulterioară. Vom înregistra acolo cheia publică, astfel încât să putem verifica dacă mesajul primit a fost semnat cu cheia privată corespunzătoare.

Înainte de a continua, să creăm o cheie privată și să o scriem test/keys/owner.pk. Pentru a face acest lucru, să lansăm Fift în modul interactiv și să executăm patru comenzi.

`newkeypair` генерация публичного и приватного ключа и запись их в стек. 

`drop` удаления из стека верхнего элемента (в данном случае публичный ключ)  

`.s` просто посмотреть что лежит в стеке в данный момент 

`"owner.pk" B>file` запись приватного ключа в файл с именем `owner.pk`. 

`bye` завершает работу с Fift. 

Să creăm un folder keys în interiorul folderului test și scrieți cheia privată acolo.

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 un fișier în folderul curent owner.pk.

Scoatem cheia publică din stivă și atunci când este nevoie o putem obține din cea privată.

Acum trebuie să scriem o verificare a semnăturii. Să începem cu testul. Mai întâi citim cheia privată din fișier folosind funcția file>B și scrieți-l într-o variabilă owner_private_key, apoi folosind funcția priv>pub convertiți cheia privată într-o cheie publică și scrieți 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 !

Vom avea nevoie de ambele chei.

Inițializam stocarea smart contract cu date arbitrare în aceeași secvență ca și în funcție pack_state()și scrieți-l într-o variabilă storage.

variable owner_private_key
variable owner_public_key 
variable orders
variable owner_wc
variable owner_account_id

"./keys/owner.pk" file>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !
dictnew orders !
0 owner_wc !
0 owner_account_id !

<b 0 32 u, owner_public_key @ B, 0 32 u, 0 32 u, 0 Gram, 0 Gram, owner_wc @ 32 i, owner_account_id @ 256 u,  orders @ dict, b> storage !

În continuare, vom compune un mesaj semnat, acesta 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 o cheie privată și în final generăm un mesaj semnat.

variable message_to_sign
variable message_to_send
variable signature
<b 0 32 u, b> message_to_sign !
message_to_sign @ hashu owner_private_key @ ed25519_sign_uint signature !
<b signature @ B, 0 32 u, b> <s  message_to_send !  

Drept urmare, mesajul pe care îl vom trimite către contractul inteligent este înregistrat într-o variabilă message_to_send, despre funcții hashu, ed25519_sign_uint poți citi în documentația Fift.

Și pentru a rula testul sunăm din nou.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Aici Fișierul cu teste ar trebui să arate așa în această etapă.

Să rulăm testul și va eșua, așa că vom schimba contractul inteligent astfel încât să poată primi mesaje de acest format și să verifice semnătura.

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

Deoarece avem o funcție pentru citirea datelor din stocarea smart contract, o vom folosi.

Urmează verificarea contorului transferat cu stocarea și verificarea semnăturii. Dacă ceva nu se potrivește, atunci vom arunca 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));

Angajare relevantă aici.

Să rulăm testele și să vedem că al doilea test eșuează. Din două motive, nu există destui biți în mesaj și nu există destui biți în stocare, așa că codul se blochează la analizare. Trebuie să adăugăm o semnătură la mesajul pe care îl trimitem și să copiem stocarea de la ultimul test.

În al doilea test, vom adăuga o semnătură de mesaj și vom schimba stocarea contractului inteligent. Aici fisierul cu teste arata ca in acest moment.

Să scriem un al patrulea test, în care vom trimite un mesaj semnat cu cheia privată a altcuiva. Să creăm o altă cheie privată și să o salvăm într-un fișier not-owner.pk. Vom semna mesajul cu această cheie privată. Să rulăm testele și să ne asigurăm că toate testele trec. Angajează-te în prezent.

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

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

Pentru orice eventualitate, avem nevoie de posibilitatea de a schimba adresa la care trimitem grame din ratați. De asemenea, ar trebui să putem trimite grame de la loterie la adresa proprietarului.

Să începem cu primul. Să scriem mai întâi un test care să verifice că după trimiterea mesajului, contractul inteligent a salvat noua adresă în stocare. Vă rugăm să rețineți că în mesaj, pe lângă ghișeu și noua adresă, transmitem și noi action Un număr întreg nenegativ de 7 biți, în funcție de acesta, vom alege cum să procesăm mesajul în contractul inteligent.

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

În test puteți vedea cum stocarea smartcontract este deserializată storage în Cinci. Deserializarea variabilelor este descrisă în documentația Fift.

Link de comitere cu adaos de aluat.

Să rulăm testul și să ne asigurăm că eșuează. Acum să adăugăm logică pentru a schimba adresa proprietarului loteriei.

În contractul inteligent continuăm să analizăm message, citiți în action. Să vă reamintim că vom avea două action: schimba adresa si trimite grame.

Apoi citim noua adresă a titularului contractului și o salvăm în depozit.
Executăm testele și vedem că al treilea test eșuează. Se blochează din cauza faptului că acum contractul analizează suplimentar 7 biți din mesaj, care lipsesc în test. Adăugați unul inexistent la mesaj action. Să facem testele și să vedem că totul trece. Aici angajați-vă la schimbări. Grozav.

Acum să scriem logica pentru trimiterea numărului specificat de grame la adresa salvată anterior.

Mai întâi, să scriem un test. Vom scrie două teste, unul când nu este suficient echilibru, al doilea când totul ar trebui să treacă cu succes. Testele pot fi vizualizate în acest angajament.

Acum să adăugăm codul. Mai întâi, să scriem două metode de ajutor. Prima metodă de obținere este să aflați soldul curent al unui contract inteligent.

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

Iar al doilea este pentru trimiterea de grame la un alt contract inteligent. Am copiat complet această metodă dintr-un alt contract inteligent.

() 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 <= format indicator int_msg_info$0 
    ;;  .store_uint(1, 1) ;; 1 <= ihr disabled
    ;;  .store_uint(1, 1) ;; 1 <= bounce = true
    ;;  .store_uint(0, 1) ;; 0 <= bounced = false
    ;;  .store_uint(4, 5)  ;; 00100 <= address flags, anycast = false, 8-bit workchain
        .store_uint (196, 9)
        .store_int(wc, 8)
        .store_uint(addr, 256)
        .store_grams(grams)
        .store_uint(0, 107) ;; 106 zeroes +  0 as an indicator that there is no cell with the data.
        .end_cell(); 
    send_raw_message(msg, 3); ;; mode, 2 for ignoring errors, 1 for sender pays fees, 64 for returning inbound message value
}

Să adăugăm aceste două metode la contractul inteligent și să scriem logica. Mai întâi, analizăm numărul de grame din mesaj. În continuare verificăm soldul, dacă nu este suficient aruncăm o excepție. Dacă totul este în regulă, atunci trimitem gramele la 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));

Aici arată ca contractul inteligent în acest moment. Să rulăm testele și să ne asigurăm că trec.

Apropo, un comision este dedus din contractul inteligent de fiecare dată pentru un mesaj procesat. Pentru ca mesajele smart contract să execute cererea, după verificări de bază trebuie să suni accept_message().

Acum să trecem la mesajele interne. De fapt, vom accepta doar grame și vom trimite înapoi dublul sumei jucătorului dacă câștigă și o treime proprietarului dacă pierde.

Mai întâi, să scriem un test simplu. Pentru a face acest lucru, avem nevoie de o adresă de testare a contractului inteligent de la care se presupune că trimitem grame către contractul inteligent.

Adresa contractului inteligent constă din două numere, un număr întreg de 32 de biți responsabil pentru lanțul de lucru și un număr unic de cont întreg nenegativ de 256 de biți în acest lanț de lucru. De exemplu, -1 și 12345, aceasta este adresa pe care o vom salva într-un 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ă ne uităm la modul în care funcționează funcția, aceasta va oferi o înțelegere a modului în care funcționează Fift. Lansați Fift în modul interactiv.

~/TON/build/crypto/fift -i 

Mai întâi împingem -1, 12345 și numele viitorului fișier „sender.addr” în stivă:

-1 12345 "sender.addr" 

Următorul pas este executarea funcției -rot, care schimbă stiva astfel încât în ​​partea de sus a stivei să existe un număr unic de contract inteligent:

"sender.addr" -1 12345

256 u>B convertește un întreg nenegativ de 256 de biți în octeți.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap schimbă primele două elemente ale stivei.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

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

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ conectează două secvențe de octeți.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

din nou swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Și în cele din urmă octeții sunt scrieți în fișier B>file. După aceasta, stiva noastră este goală. Să ne oprim Fift. A fost creat un fișier în folderul curent sender.addr. Să mutăm fișierul în folderul creat test/addresses/.

Să scriem un test simplu care va trimite grame la un contract inteligent. Aici e angajamentul.

Acum să ne uităm la logica loteriei.

Primul lucru pe care îl facem este să verificăm mesajul bounced sau nu dacă bounced, apoi îl ignorăm. bounced înseamnă că contractul va returna grame dacă apare o eroare. Nu vom returna grame dacă apare brusc o eroare.

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

În continuare, analizăm adresa contractului inteligent de la care a venit mesajul.

Citim datele din stocare și apoi ștergem pariurile vechi din istoric dacă sunt mai mult de douăzeci. Pentru comoditate, am scris trei funcții suplimentare pack_order(), unpack_order(), remove_old_orders().

În continuare, ne uităm dacă soldul nu este suficient pentru plată, atunci considerăm că acesta nu este un pariu, ci o reaprovizionare și salvăm reaprovizionarea în orders.

Apoi, în sfârșit, esența contractului inteligent.

În primul rând, dacă jucătorul pierde, îl salvăm în istoricul pariurilor și dacă suma este mai mare de 3 grame, trimitem 1/3 proprietarului contractului inteligent.

Dacă jucătorul câștigă, atunci trimitem dublul sumei la adresa jucătorului și apoi salvăm informațiile 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() < 2 * order_amount + 500000000) { ;; not enough grams to pay the bet back, so this is re-fill
        builder order = pack_order(order_seqno, 1, 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));
        return ();
    }
    if (rand(10) >= 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));
}

Asta e tot. Angajarea corespunzătoare.

Acum tot ce rămâne este simplu, haideți să creăm metode de obținere, astfel încât să putem obține informații despre starea contractului din lumea exterioară (de fapt, citiți datele din stocarea lor smart contract).

Să adăugăm metode get. Vom scrie mai jos despre cum să primim informații despre un contract inteligent.

De asemenea, am uitat să adaug codul care va procesa prima solicitare care apare la publicarea unui contract inteligent. Angajarea corespunzătoare. Și mai departe corectat eroare cu trimiterea a 1/3 din sumă în contul proprietarului.

Următorul pas este publicarea contractului inteligent. Să creăm un folder requests.

Am luat ca bază codul publicației simple-wallet-code.fc care poate găsi în depozitul oficial.

Ceva la care merită să fii atent. Generăm o stocare de contract inteligent și un mesaj de intrare. După aceasta, se generează adresa contractului inteligent, adică adresa este cunoscută chiar înainte de publicare în TON. În continuare, trebuie să trimiteți câteva grame la această adresă și numai după aceea trebuie să trimiteți un fișier cu contractul inteligent în sine, deoarece rețeaua ia un comision pentru stocarea contractului inteligent și a operațiunilor în acesta (validatori care stochează și execută smart contract). contracte). Codul poate fi vizualizat aici.

Apoi executăm codul de publicare și obținem lottery-query.boc dosar și adresa smart contract.

~/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, vom vedea adresa contractului inteligent în jurnalele de execuție.

new wallet address = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a 
(Saving address to file lottery.addr)
Non-bounceable address (for init): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Bounceable address (for later access): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFY

Doar pentru distracție, să facem o cerere către TON

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

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

account state is empty

Trimitem la adresa 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 grame și după câteva secunde executăm aceeași comandă. Pentru a trimite grame folosesc portofelul oficial, și poți cere cuiva din chat grame de test, despre care voi vorbi la sfârșitul articolului.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Pare un neinițializat (state:account_uninit) un contract inteligent cu aceeași adresă și un sold de 1 de nanograme.

account state is (account
  addr:(addr_std
    anycast:nothing workchain_id:0 address: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)) last_paid:1583257959
    due_payment:nothing)
  storage:(account_storage last_trans_lt:3825478000002
    balance:(currencies
      grams:(nanograms
        amount:(var_uint len:4 value:2000000000))
      other:(extra_currencies
        dict:hme_empty))
    state:account_uninit))
x{C00044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A20259C2F2F4CB3800000DEAC10776091DCD650004_}
last transaction lt = 3825478000001 hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
account balance is 2000000000ng

Acum să publicăm contractul inteligent. Să lansăm lite-client și să executăm.

> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] sending query from file lottery-query.boc
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query]    external message status is 1 

Să verificăm dacă contractul a fost publicat.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Printre altele, primim.

  storage:(account_storage last_trans_lt:3825499000002
    balance:(currencies
      grams:(nanograms
        amount:(var_uint len:4 value:1987150999))
      other:(extra_currencies
        dict:hme_empty))
    state:(account_active

Noi vedem asta account_active.

Commit-ul corespunzător cu modificări aici.

Acum să creăm solicitări pentru a interacționa cu contractul inteligent.

Mai exact, o vom lăsa pe prima pentru schimbarea adresei ca o lucrare independentă, iar pe a doua o vom face pentru trimiterea de grame la adresa proprietarului. De fapt, va trebui să facem același lucru ca în testul pentru trimiterea de grame.

Acesta este mesajul pe care îl vom trimite către contractul inteligent, unde msg_seqno 165, action 2 și 9.5 grame pentru expediere.

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

Nu uitați să semnați mesajul cu cheia privată lottery.pk, care a fost generat mai devreme la crearea contractului inteligent. Iată commit-ul corespunzător.

Primirea de informații dintr-un contract inteligent folosind metode get

Acum să ne uităm la cum să rulăm metode de obținere a contractelor inteligente.

Lansa lite-client și rulați metodele get pe care le-am scris.

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

В result conține valoarea pe care o returnează funcția balance() din contractul nostru inteligent.
Vom face același lucru pentru mai multe metode.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
arguments:  [ 77871 ] 
result:  [ 1 ] 

Să vă cerem istoricul pariurilor.

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

Vom folosi lite-client și vom obține metode pentru a afișa informații despre contractul inteligent pe site.

Afișarea datelor privind contractele inteligente pe site

Am scris un site web simplu în Python pentru a afișa datele din contractul inteligent într-un mod convenabil. Aici nu mă voi opri în detaliu și voi publica site-ul într-un singur comite.

Solicitările către TON se fac de la Python via lite-client. Pentru comoditate, site-ul este ambalat în Docker și publicat pe Google Cloud. Legătură.

Încercând

Acum să încercăm să trimitem grame acolo pentru completare de la pungă. Vom trimite 40 de grame. Și să facem câteva pariuri pentru claritate. Vedem că site-ul arată istoricul pariurilor, procentul actual de câștig și alte informații utile.

V-om vedeacă am câștigat primul, l-am pierdut pe al doilea.

postfață

Articolul s-a dovedit a fi mult mai lung decât mă așteptam, poate ar fi putut fi mai scurt, sau poate doar pentru o persoană care nu știe nimic despre TON și vrea să scrie și să publice un smart contract nu atât de simplu, cu capacitatea de a interacționa cu aceasta. Poate că unele lucruri ar fi putut fi explicate mai simplu.

Poate că unele aspecte ale implementării ar fi putut fi făcute mai eficient și mai elegant, dar atunci ar fi durat și mai mult timp pregătirea articolului. De asemenea, este posibil să fi greșit undeva sau să nu fi înțeles ceva, așa că dacă faci ceva serios, trebuie să te bazezi pe documentația oficială sau pe depozitul oficial cu codul TON.

Trebuie remarcat faptul că, deoarece TON în sine se află încă în stadiul activ de dezvoltare, pot apărea modificări care vor rupe oricare dintre pașii din acest articol (ceea ce s-a întâmplat în timp ce scriam, a fost deja corectat), dar abordarea generală este puțin probabil să se schimbe.

Nu voi vorbi despre viitorul TON. Poate că platforma va deveni ceva mare și ar trebui să petrecem timp studiind-o și să umplem o nișă cu produsele noastre 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 Balanță, judecând după forum există mult mai multă activitate acolo decât în ​​comunitatea TON. Deși dezvoltatorii și comunitatea TON sunt mai degrabă underground, ceea ce este, de asemenea, mișto.

referințe

  1. Documentație oficială TON: https://test.ton.org
  2. Depozitul oficial TON: https://github.com/ton-blockchain/ton
  3. Portofel oficial pentru diferite platforme: https://wallet.ton.org
  4. Depozitul de contracte inteligent din acest articol: https://github.com/raiym/astonished
  5. Link către site-ul web al contractului inteligent: https://ton-lottery.appspot.com
  6. Depozitul pentru extensia pentru Visual Studio Code pentru FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Discutați despre TON în Telegram, ceea ce a ajutat cu adevărat să vă dați seama în etapa inițială. Cred că nu va fi o greșeală dacă spun că toți cei care au scris ceva pentru TON sunt acolo. Puteți cere și grame de test acolo. https://t.me/tondev_ru
  8. Un alt chat despre TON în care am găsit informații utile: https://t.me/TONgramDev
  9. Prima etapă a competiției: https://contest.com/blockchain
  10. Etapa a doua a concursului: https://contest.com/blockchain-2

Sursa: www.habr.com

Adauga un comentariu