Om hur man skriver och publicerar ett smart kontrakt i Telegram Open Network (TON)

Om hur man skriver och publicerar ett smart kontrakt i TON

Vad handlar den här artikeln om?

I artikeln kommer jag att prata om hur jag deltog i den första (av två) Telegram blockchain-tävling, inte tog ett pris och bestämde mig för att spela in min erfarenhet i en artikel så att den inte sjunker i glömska och kanske hjälper någon.

Eftersom jag inte ville skriva abstrakt kod, utan att göra något som fungerar, skrev jag för artikeln ett smart kontrakt för ett omedelbart lotteri och en webbplats som visar smart kontraktsdata direkt från TON utan att använda mellanlagring.

Artikeln kommer att vara användbar för dem som vill göra sitt första smarta kontrakt i TON, men inte vet var de ska börja.

Med lotteriet som exempel kommer jag att gå från att installera miljön till att publicera ett smart kontrakt, interagera med det och skriva en hemsida för att ta emot och publicera data.

Om deltagande i tävlingen

I oktober förra året utlyste Telegram en blockchain-tävling med nya språk Fift и FunC. Det var nödvändigt att välja mellan att skriva något av de fem föreslagna smarta kontrakten. Jag tänkte att det skulle vara trevligt att göra något annorlunda, lära mig ett språk och göra något, även om jag inte behöver skriva något mer i framtiden. Dessutom är ämnet ständigt på läpparna.

Det är värt att säga att jag inte hade någon erfarenhet av att utveckla smarta kontrakt.

Jag planerade att delta ända till slutet tills jag kunde och sedan skriva en recensionsartikel, men jag misslyckades direkt vid den första. jag skrev en plånbok med multisignatur på FunC och det fungerade i allmänhet. Jag tog det som grund smart kontrakt om soliditet.

Då trodde jag att det här definitivt räckte för att ta åtminstone någon prisplats. Som ett resultat blev cirka 40 av 60 deltagare pristagare och jag var inte bland dem. I allmänhet är det inget fel med det här, men en sak störde mig. Vid tidpunkten för tillkännagivandet av resultatet hade inte granskningen av testet för mitt kontrakt gjorts, jag frågade deltagarna i chatten om det var någon annan som inte hade det, det fanns inga.

Uppenbarligen uppmärksammade mina meddelanden, två dagar senare publicerade domarna en kommentar och jag förstår fortfarande inte om de av misstag missade mitt smarta kontrakt under bedömningen eller helt enkelt tyckte att det var så illa att det inte behövde en kommentar. Jag ställde en fråga på sidan, men fick inget svar. Även om det inte är någon hemlighet vem som dömde, ansåg jag att det var onödigt att skriva personliga meddelanden.

Mycket tid gick åt till att förstå, så det beslöts att skriva en artikel. Eftersom det inte finns mycket information ännu, kommer den här artikeln att spara tid för alla intresserade.

Konceptet med smarta kontrakt i TON

Innan du skriver något måste du ta reda på vilken sida du ska närma dig den här saken från. Därför ska jag nu berätta vilka delar systemet består av. Närmare bestämt, vilka delar du behöver veta för att kunna skriva åtminstone något slags arbetskontrakt.

Vi kommer fokusera på att skriva ett smart kontrakt och jobba med TON Virtual Machine (TVM), Fift и FunC, så artikeln är mer som en beskrivning av utvecklingen av ett vanligt program. Vi kommer inte att uppehålla oss vid hur själva plattformen fungerar här.

Generellt om hur det fungerar TVM och språk Fift det finns bra officiell dokumentation. När jag deltog i tävlingen och nu när jag skrev det nuvarande kontraktet vände jag mig ofta till henne.

Huvudspråket som smarta kontrakt skrivs på är FunC. Det finns ingen dokumentation på det för tillfället, så för att skriva något måste du studera exempel på smarta kontrakt från det officiella förvaret och implementeringen av själva språket där, plus att du kan titta på exempel på smarta kontrakt från de senaste två tävlingar. Länkar i slutet av artikeln.

Låt oss säga att vi redan har skrivit ett smart kontrakt för FunC, efter det kompilerar vi koden till Fift assembler.

Det sammanställda smarta kontraktet återstår att publiceras. För att göra detta måste du skriva en funktion i Fift, som tar den smarta kontraktskoden och några andra parametrar som indata, och utgången kommer att vara en fil med tillägget .boc (vilket betyder "påse med celler") och, beroende på hur vi skriver det, en privat nyckel och adress, som genereras baserat på den smarta kontraktskoden. Du kan redan skicka gram till adressen till ett smart kontrakt som ännu inte har publicerats.

För att publicera ett smart kontrakt i TON mottagits .boc filen kommer att behöva skickas till blockkedjan med en lätt klient (mer om det nedan). Men innan du publicerar måste du överföra gram till den genererade adressen, annars kommer det smarta kontraktet inte att publiceras. Efter publicering kan du interagera med det smarta kontraktet genom att skicka meddelanden utifrån (till exempel med hjälp av en lätt klient) eller från insidan (till exempel skickar ett smart kontrakt ett meddelande till ett annat inuti TON).

När vi väl förstår hur koden publiceras blir det lättare. Vi vet ungefär vad vi vill skriva och hur vårt program kommer att fungera. Och medan vi skriver letar vi efter hur detta redan är implementerat i befintliga smarta kontrakt, eller så tittar vi på implementeringskoden Fift и FunC i det officiella arkivet, eller titta i den officiella dokumentationen.

Mycket ofta sökte jag på nyckelord i Telegram-chatten där alla tävlingsdeltagare och Telegram-anställda samlades, och det hände sig att alla under tävlingen samlades där och började diskutera Fift och FunC. Länk i slutet av artikeln.

Det är dags att gå från teori till praktik.

Förbereda miljön för att arbeta med TON

Jag gjorde allt som kommer att beskrivas i artikeln om MacOS och dubbelkollade det i ren Ubuntu 18.04 LTS på Docker.

Det första du behöver göra är att ladda ner och installera lite-client med vilken du kan skicka förfrågningar till TON.

Instruktionerna på den officiella webbplatsen beskriver installationsprocessen ganska detaljerat och tydligt och utelämnar vissa detaljer. Här följer vi instruktionerna och installerar de saknade beroenden längs vägen. Jag kompilerade inte varje projekt själv och installerade från det officiella Ubuntu-förvaret (på MacOS jag använde 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 

När alla beroenden är installerade kan du installera lite-client, Fift, FunC.

Först klonar vi TON-förvaret tillsammans med dess beroenden. För enkelhetens skull gör vi allt i en pärm ~/TON.

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

Förvaret lagrar även implementeringar Fift и FunC.

Nu är vi redo att montera projektet. Förvarskoden klonas in i en mapp ~/TON/ton. I ~/TON skapa en mapp build och samla projektet i den.

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

Eftersom vi ska skriva ett smart kontrakt behöver vi inte bara lite-clientMen Fift с FunC, så låt oss sammanställa allt. Det är ingen snabb process, så vi väntar.

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

Ladda sedan ner konfigurationsfilen som innehåller data om den nod till vilken lite-client kommer att ansluta.

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

Gör de första förfrågningarna till TON

Låt oss nu starta lite-client.

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

Om bygget lyckades kommer du efter lanseringen att se en logg över anslutningen av light-klienten till noden.

[ 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)
...

Du kan köra kommandot help och se vilka kommandon som finns tillgängliga.

help

Låt oss lista kommandona som vi kommer att använda i den här artikeln.

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-методы смартконтракта. 

Nu är vi redo att skriva själva kontraktet.

genomförande

Idé

Som jag skrev ovan är det smarta kontraktet som vi skriver ett lotteri.

Dessutom är detta inte ett lotteri där du behöver köpa en biljett och vänta en timme, dag eller månad, utan ett omedelbart lotteri där användaren överför till kontraktsadressen N gram och får den omedelbart tillbaka 2 * N gram eller tappar. Vi kommer att göra sannolikheten för att vinna cirka 40%. Om det inte finns tillräckligt med gram för betalning, kommer vi att betrakta transaktionen som en påfyllning.

Dessutom är det viktigt att vad kan ses i realtid och i en bekväm form, så att användaren omedelbart kan förstå om han vann eller förlorade. Därför måste du göra en hemsida som visar satsningar och resultat direkt från TON.

Att skriva ett smart kontrakt

För enkelhetens skull har jag markerat koden för FunC; plugin-programmet kan hittas och installeras i Visual Studio Code-sökningen; om du plötsligt vill lägga till något har jag gjort plugin-programmet allmänt tillgängligt. Dessutom har någon tidigare gjort ett plugin för att arbeta med Fift, du kan också installera det och hitta det i VSC.

Låt oss omedelbart skapa ett arkiv där vi kommer att begå de mellanliggande resultaten.

För att göra livet enklare kommer vi att skriva ett smart kontrakt och testa det lokalt tills det är klart. Först efter det kommer vi att publicera den i TON.

Det smarta kontraktet har två externa metoder som kan nås. Först, recv_external() denna funktion exekveras när en förfrågan till kontraktet kommer från omvärlden, det vill säga inte från till exempel TON, när vi själva genererar ett meddelande och skickar det via lite-klienten. Andra, recv_internal() det är då, inom TON självt, något kontrakt hänvisar till vårt. I båda fallen kan du skicka parametrar till funktionen.

Låt oss börja med ett enkelt exempel som fungerar om det publiceras, men det finns ingen funktionell belastning i det.

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

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

Här måste vi förklara vad det är slice. All data som lagras i TON Blockchain är en samling TVM cell eller helt enkelt cell, i en sådan cell kan du lagra upp till 1023 bitar av data och upp till 4 länkar till andra celler.

TVM cell slice eller slice detta är en del av den befintliga cell används för att analysera det kommer det att bli klart senare. Huvudsaken för oss är att vi kan överföra slice och beroende på typen av meddelande, bearbeta data in recv_external() eller recv_internal().

impure — ett nyckelord som indikerar att funktionen modifierar smarta kontraktsdata.

Låt oss spara kontraktskoden lottery-code.fc och kompilera.

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

Betydelsen av flaggorna kan ses med kommandot

~/TON/build/crypto/func -help

Vi har sammanställt Fift assembler-kod i 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

Det kan lanseras lokalt, för detta kommer vi att förbereda miljön.

Observera att den första raden ansluter Asm.fif, detta är kod skriven i Fift för Fift assembler.

Eftersom vi vill köra och testa det smarta kontraktet lokalt kommer vi att skapa en fil lottery-test-suite.fif och kopiera den kompilerade koden dit, ersätt den sista raden i den, som skriver den smarta kontraktskoden till en konstant codeför att sedan överföra den till den virtuella maskinen:

"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

Så långt verkar det vara klart, låt oss nu lägga till koden som vi kommer att använda för att starta TVM i samma fil.

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 vi registrerar sammanhanget, det vill säga de data med vilken TVM (eller nätverkstillstånd) kommer att startas. Redan under tävlingen visade en av utvecklarna hur man skapar c7 och jag kopierade. I den här artikeln kan vi behöva ändra rand_seed eftersom genereringen av ett slumptal beror på det och om det inte ändras kommer samma nummer att returneras varje gång.

recv_internal и recv_external konstanter med värdena 0 och -1 kommer att ansvara för att anropa motsvarande funktioner i det smarta kontraktet.

Nu är vi redo att skapa det första testet för vårt tomma smarta kontrakt. För tydlighetens skull kommer vi för närvarande att lägga till alla tester i samma fil lottery-test-suite.fif.

Låt oss skapa en variabel storage och skriv en tom i den cell, kommer detta att vara den smarta kontraktslagringen.

message Detta är budskapet som vi kommer att överföra till den smarta kontakten utifrån. Vi kommer också att göra det tomt för tillfället.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Efter att vi har förberett konstanterna och variablerna startar vi TVM med kommandot runvmctx och skicka de skapade parametrarna till ingången.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

I slutändan kommer vi att lyckas så här mellankod för Fift.

Nu kan vi köra den resulterande koden.

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

Programmet bör köras utan fel och i utgången kommer vi att se exekveringsloggen:

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

Bra, vi har skrivit den första fungerande versionen av det smarta kontraktet.

Nu måste vi lägga till funktionalitet. Låt oss först ta itu med meddelanden som kommer från omvärlden till recv_external()

Byggherren väljer själv vilket meddelandeformat som kontraktet kan acceptera.

Men oftast

  • För det första vill vi skydda vårt kontrakt från omvärlden och göra det så att endast ägaren av kontraktet kan skicka externa meddelanden till det.
  • för det andra, när vi skickar ett giltigt meddelande till TON vill vi att detta ska hända exakt en gång och när vi skickar samma meddelande igen, avvisar det smarta kontraktet det.

Så nästan varje kontrakt löser dessa två problem, eftersom vårt kontrakt accepterar externa meddelanden måste vi ta hand om det också.

Vi gör det i omvänd ordning. Låt oss först lösa problemet med upprepning; om kontraktet redan har fått ett sådant meddelande och bearbetat det, kommer det inte att utföras en andra gång. Och då ska vi lösa problemet så att bara en viss krets av människor kan skicka meddelanden till det smarta kontraktet.

Det finns olika sätt att lösa problemet med dubbletter av meddelanden. Så här gör vi. I det smarta kontraktet initierar vi räknaren för mottagna meddelanden med startvärdet 0. I varje meddelande till det smarta kontraktet kommer vi att lägga till det aktuella räknarvärdet. Om räknarvärdet i meddelandet inte stämmer överens med värdet i det smarta kontraktet så behandlar vi det inte; om det gör det så bearbetar vi det och ökar räknaren i det smarta kontraktet med 1.

Låt oss återvända till lottery-test-suite.fif och lägg till ett andra test till det. Om vi ​​skickar ett felaktigt nummer bör koden ge ett undantag. Låt till exempel kontraktsdata lagra 166, så skickar vi 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"

Låt oss starta.

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

Och vi kommer att se att testet utförs med ett fel.

[ 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

I detta skede lottery-test-suite.fif ska se ut по ссылке.

Låt oss nu lägga till motlogiken till det smarta kontraktet 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 ligger budskapet vi skickar.

Det första vi gör är att kontrollera om meddelandet innehåller data, om inte, så avslutar vi helt enkelt.

Därefter analyserar vi meddelandet. in_msg~load_uint(32) laddar numret 165, 32-bitars unsigned int från det överförda meddelandet.

Därefter laddar vi 32 bitar från den smarta kontraktslagringen. Vi kontrollerar att det laddade numret stämmer överens med det godkända, om inte gör vi ett undantag. I vårt fall, eftersom vi passerar en icke-match, bör ett undantag kastas.

Låt oss nu kompilera.

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

Kopiera den resulterande koden till lottery-test-suite.fif, glöm inte att ersätta den sista raden.

Vi kontrollerar att testet klarar:

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

Precis här Du kan se motsvarande commit med de aktuella resultaten.

Observera att det är obekvämt att ständigt kopiera den kompilerade koden för ett smart kontrakt till en fil med tester, så vi kommer att skriva ett skript som skriver koden till en konstant för oss, och vi kopplar helt enkelt den kompilerade koden till våra tester med hjälp av "include".

Skapa en fil i projektmappen build.sh med följande innehåll.

#!/bin/bash

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

Låt oss göra det körbart.

chmod +x ./build.sh

Nu är det bara att köra vårt skript för att kompilera kontraktet. Men förutom detta måste vi skriva in det i en konstant code. Så vi kommer att skapa en ny fil lotter-compiled-for-test.fif, som vi kommer att inkludera i filen lottery-test-suite.fif.

Låt oss lägga till skirpt-kod till sh, som helt enkelt kommer att duplicera den kompilerade filen in lotter-compiled-for-test.fif och ändra den sista raden i den.

# 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

Nu, för att kontrollera, låt oss köra det resulterande skriptet och en fil kommer att genereras lottery-compiled-for-test.fif, som vi kommer att ta med i vår lottery-test-suite.fif

В lottery-test-suite.fif radera kontraktskoden och lägg till raden "lottery-compiled-for-test.fif" include.

Vi kör tester för att kontrollera att de klarar.

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

Bra, nu för att automatisera lanseringen av tester, låt oss skapa en fil test.sh, som först körs build.sh, och kör sedan testerna.

touch test.sh
chmod +x test.sh

Vi skriver inuti

./build.sh 

echo "nCompilation completedn"

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

Låt oss göra test.sh och kör det för att se till att testerna fungerar.

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

Vi kontrollerar att kontraktet sammanställs och att testerna är utförda.

Bra, nu på start test.sh Testerna kommer att sammanställas och köras omedelbart. Här är länken till begå.

Okej, innan vi fortsätter, låt oss göra en sak till för bekvämlighets skull.

Låt oss skapa en mapp build där vi kommer att lagra det kopierade kontraktet och dess klon inskrivet i en konstant lottery-compiled.fif, lottery-compiled-for-test.fif. Låt oss också skapa en mapp test var kommer testfilen att lagras? lottery-test-suite.fif och eventuellt andra stödfiler. Länk till relevanta ändringar.

Låt oss fortsätta utveckla det smarta kontraktet.

Härnäst ska det vara ett test som kontrollerar att meddelandet tas emot och räknaren är uppdaterad i butiken när vi skickar rätt nummer. Men det gör vi senare.

Låt oss nu tänka på vilken datastruktur och vilken data som behöver lagras i det smarta kontraktet.

Jag kommer att beskriva allt som vi lagrar.

`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` переменная типа словарь, хранит последние двадцать ставок. 

Därefter måste du skriva två funktioner. Låt oss ringa den första pack_state(), som packar data för efterföljande lagring i den smarta kontraktslagringen. Låt oss ringa den andra unpack_state() kommer att läsa och returnera data från lagring.

_ 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;
}

Vi lägger till dessa två funktioner i början av det smarta kontraktet. Det kommer att lösa sig så här mellanresultat.

För att spara data måste du anropa den inbyggda funktionen set_data() och den kommer att skriva data från pack_state() i den smarta kontraktslagringen.

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

Nu när vi har bekväma funktioner för att skriva och läsa data kan vi gå vidare.

Vi måste kontrollera att meddelandet som kommer in utifrån är signerat av ägaren av kontraktet (eller annan användare som har tillgång till den privata nyckeln).

När vi publicerar ett smart kontrakt kan vi initialisera det med den data vi behöver i lagring, som kommer att sparas för framtida bruk. Vi kommer att spela in den publika nyckeln där så att vi kan verifiera att det inkommande meddelandet signerades med motsvarande privata nyckel.

Innan vi fortsätter, låt oss skapa en privat nyckel och skriva den till test/keys/owner.pk. För att göra detta, låt oss starta Fift i interaktivt läge och köra fyra kommandon.

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

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

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

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

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

Låt oss skapa en mapp keys inuti mappen test och skriv den privata nyckeln där.

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

Vi ser en fil i den aktuella mappen owner.pk.

Vi tar bort den publika nyckeln från stacken och vid behov kan vi hämta den från den privata.

Nu måste vi skriva en signaturverifiering. Låt oss börja med testet. Först läser vi den privata nyckeln från filen med hjälp av funktionen file>B och skriv det till en variabel owner_private_keyoch sedan använda funktionen priv>pub konvertera den privata nyckeln till en offentlig nyckel och skriv resultatet i 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 !

Vi kommer att behöva båda nycklarna.

Vi initialiserar den smarta kontraktslagringen med godtyckliga data i samma sekvens som i funktionen pack_state()och skriv det i en variabel 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 !

Därefter kommer vi att skriva ett signerat meddelande, det kommer bara att innehålla signaturen och räknarvärdet.

Först skapar vi den data som vi vill överföra, sedan signerar vi den med en privat nyckel och slutligen genererar vi ett signerat meddelande.

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 !  

Som ett resultat av detta registreras meddelandet som vi kommer att skicka till det smarta kontraktet i en variabel message_to_send, om funktioner hashu, ed25519_sign_uint du kan läsa i Fift-dokumentationen.

Och för att köra testet ringer vi igen.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Här så Filen med tester ska se ut så här i detta skede.

Låt oss köra testet och det kommer att misslyckas, så vi ändrar det smarta kontraktet så att det kan ta emot meddelanden i detta format och verifiera signaturen.

Först räknar vi 512 bitar av signaturen från meddelandet och skriver den till en variabel, sedan räknar vi 32 bitar av räknarvariabeln.

Eftersom vi har en funktion för att läsa data från den smarta kontraktslagringen kommer vi att använda den.

Nästa är att kontrollera räknaren som överförts med lagringen och kontrollera signaturen. Om något inte matchar, så kastar vi ett undantag med lämplig kod.

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

Relevant engagemang just här.

Låt oss köra testerna och se att det andra testet misslyckas. Av två anledningar finns det inte tillräckligt med bitar i meddelandet och det finns inte tillräckligt med bitar i lagringen, så koden kraschar vid analys. Vi måste lägga till en signatur till meddelandet vi skickar och kopiera lagringen från det senaste testet.

I det andra testet kommer vi att lägga till en meddelandesignatur och ändra den smarta kontraktslagringen. Här så filen med tester ser ut för tillfället.

Låt oss skriva ett fjärde test, där vi skickar ett meddelande signerat med någon annans privata nyckel. Låt oss skapa en annan privat nyckel och spara den i en fil not-owner.pk. Vi kommer att signera meddelandet med denna privata nyckel. Låt oss köra testerna och se till att alla tester blir godkända. Begå för närvarande.

Nu kan vi äntligen gå vidare till att implementera den smarta kontraktslogiken.
В recv_external() vi accepterar två typer av meddelanden.

Eftersom vårt kontrakt kommer att ackumulera spelarnas förluster måste dessa pengar överföras till skaparen av lotteriet. Lotteriskaparens plånboksadress registreras i förrådet när kontraktet skapas.

För säkerhets skull behöver vi möjligheten att ändra adressen som vi skickar gram av förlorarna till. Vi ska också kunna skicka gram från lotteriet till ägarens adress.

Låt oss börja med den första. Låt oss först skriva ett test som kontrollerar att det smarta kontraktet efter att ha skickat meddelandet sparade den nya adressen i lagringen. Observera att vi i meddelandet, förutom disken och den nya adressen, även sänder action Ett 7-bitars heltals icke-negativt nummer, beroende på det, kommer vi att välja hur vi ska behandla meddelandet i det smarta kontraktet.

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

I testet kan du se hur smartcontract storage deserialiseras storage i femman. Deserialisering av variabler beskrivs i Fift-dokumentationen.

Commit länk med tillsatt deg.

Låt oss köra testet och se till att det misslyckas. Låt oss nu lägga till logik för att ändra adressen till lotteriägaren.

I det smarta kontraktet fortsätter vi att analysera message, läs in action. Låt oss påminna dig om att vi kommer att ha två action: ändra adress och skicka gram.

Sedan läser vi den nya adressen till kontraktsägaren och sparar den i förråd.
Vi kör testerna och ser att det tredje testet misslyckas. Det kraschar på grund av att kontraktet nu dessutom analyserar 7 bitar från meddelandet, som saknas i testet. Lägg till en icke-existerande i meddelandet action. Låt oss köra testerna och se att allt går. Här engagera sig i förändringar. Bra.

Låt oss nu skriva logiken för att skicka det angivna antalet gram till den tidigare sparade adressen.

Låt oss först skriva ett test. Vi kommer att skriva två test, ett när det inte finns tillräckligt med balans, det andra när allt ska klara sig. Tester kan ses i detta åtagande.

Låt oss nu lägga till koden. Låt oss först skriva två hjälpmetoder. Den första get-metoden är att ta reda på det aktuella saldot för ett smart kontrakt.

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

Och den andra är för att skicka gram till ett annat smart kontrakt. Jag kopierade helt den här metoden från ett annat smart kontrakt.

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

Låt oss lägga till dessa två metoder till det smarta kontraktet och skriva logiken. Först analyserar vi antalet gram från meddelandet. Därefter kontrollerar vi saldot, om det inte räcker gör vi ett undantag. Om allt är bra skickar vi grammet till den sparade adressen och uppdaterar räknaren.

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

Här så ser ut som det smarta kontraktet för tillfället. Låt oss köra testerna och se till att de blir godkända.

Förresten, en provision dras från det smarta kontraktet varje gång för ett behandlat meddelande. För att de smarta kontraktsmeddelandena ska utföra begäran måste du ringa efter grundläggande kontroller accept_message().

Låt oss nu gå vidare till interna meddelanden. Faktum är att vi bara accepterar gram och skickar tillbaka dubbla beloppet till spelaren om han vinner och en tredjedel till ägaren om han förlorar.

Låt oss först skriva ett enkelt test. För att göra detta behöver vi en testadress för det smarta kontraktet från vilket vi ska skicka gram till det smarta kontraktet.

Den smarta kontraktsadressen består av två nummer, ett 32-bitars heltal som ansvarar för arbetskedjan och ett 256-bitars icke-negativt heltals unikt kontonummer i denna arbetskedja. Till exempel -1 och 12345, detta är adressen vi kommer att spara i en fil.

Jag kopierade funktionen för att spara adressen från 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

Låt oss titta på hur funktionen fungerar, detta kommer att ge en förståelse för hur Fift fungerar. Starta Fift i interaktivt läge.

~/TON/build/crypto/fift -i 

Först trycker vi -1, 12345 och namnet på den framtida filen "sender.addr" till stacken:

-1 12345 "sender.addr" 

Nästa steg är att utföra funktionen -rot, som förskjuter stacken på ett sådant sätt att högst upp i stacken finns ett unikt smart kontraktsnummer:

"sender.addr" -1 12345

256 u>B konverterar ett 256-bitars icke-negativt heltal till byte.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap byter ut de två översta elementen i stacken.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B konverterar ett 32-bitars heltal till byte.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ kopplar ihop två bytesekvenser.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

igen swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Och slutligen skrivs byten till filen B>file. Efter detta är vår stack tom. Vi slutar Fift. En fil har skapats i den aktuella mappen sender.addr. Låt oss flytta filen till den skapade mappen test/addresses/.

Låt oss skriva ett enkelt test som skickar gram till ett smart kontrakt. Här är engagemanget.

Låt oss nu titta på lotteriets logik.

Det första vi gör är att kontrollera meddelandet bounced eller inte om bounced, då ignorerar vi det. bounced innebär att kontraktet kommer att returnera gram om något fel uppstår. Vi kommer inte att returnera gram om ett fel plötsligt uppstår.

Vi kontrollerar, om saldot är mindre än ett halvt gram, accepterar vi helt enkelt meddelandet och ignorerar det.

Därefter analyserar vi adressen till det smarta kontraktet som meddelandet kom från.

Vi läser data från lagringen och raderar sedan gamla insatser från historiken om det finns fler än tjugo av dem. För enkelhetens skull skrev jag ytterligare tre funktioner pack_order(), unpack_order(), remove_old_orders().

Därefter tittar vi om saldot inte räcker för betalningen, då anser vi att detta inte är en insats, utan en påfyllning och sparar påfyllningen i orders.

Sedan äntligen kärnan i det smarta kontraktet.

Först, om spelaren förlorar, sparar vi det i spelhistoriken och om beloppet är mer än 3 gram skickar vi 1/3 till ägaren av det smarta kontraktet.

Om spelaren vinner skickar vi dubbla beloppet till spelarens adress och sparar sedan informationen om vadet i historien.

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

Det var allt. Motsvarande åtagande.

Nu är allt som återstår enkelt, låt oss skapa get-metoder så att vi kan få information om kontraktets tillstånd från omvärlden (i själva verket läs data från deras smarta kontraktslagring).

Låt oss lägga till get-metoder. Vi kommer att skriva nedan om hur du får information om ett smart kontrakt.

Jag glömde också lägga till koden som kommer att behandla den allra första förfrågan som uppstår när man publicerar ett smart kontrakt. Motsvarande åtagande. Och vidare rättad bugg med att skicka 1/3 av beloppet till ägarens konto.

Nästa steg är att publicera det smarta kontraktet. Låt oss skapa en mapp requests.

Jag tog publiceringskoden som grund simple-wallet-code.fc который kan hittas i det officiella arkivet.

Något värt att uppmärksamma. Vi genererar en smart avtalslagring och ett inmatningsmeddelande. Efter detta genereras adressen till det smarta kontraktet, det vill säga adressen är känd redan innan publicering i TON. Därefter måste du skicka flera gram till den här adressen, och först efter det behöver du skicka en fil med själva det smarta kontraktet, eftersom nätverket tar en provision för att lagra det smarta kontraktet och operationer i det (validatorer som lagrar och kör smart kontrakt). Koden kan ses här.

Därefter kör vi publiceringskoden och hämtar lottery-query.boc smart kontraktsfil och adress.

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

Glöm inte att spara de genererade filerna: lottery-query.boc, lottery.addr, lottery.pk.

Vi kommer bland annat att se adressen till det smarta kontraktet i exekveringsloggarna.

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

Bara för skojs skull, låt oss göra en förfrågan till TON

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

Och vi kommer att se att kontot med den här adressen är tomt.

account state is empty

Vi skickar till adressen 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 gram och efter några sekunder kör vi samma kommando. För att skicka gram använder jag officiell plånbok, och du kan be någon från chatten om testgram, vilket jag kommer att prata om i slutet av artikeln.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Ser ut som en oinitierad (state:account_uninit) ett smart kontrakt med samma adress och ett saldo på 1 000 000 000 nanogram.

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

Låt oss nu publicera det smarta kontraktet. Låt oss starta lite-klient och köra.

> 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 

Låt oss kontrollera att kontraktet har publicerats.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Vi får bland annat.

  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

Vi ser det account_active.

Motsvarande åtagande med förändringar just här.

Låt oss nu skapa förfrågningar för att interagera med det smarta kontraktet.

Närmare bestämt kommer vi att lämna den första för att ändra adressen som ett oberoende verk, och vi kommer att göra den andra för att skicka gram till ägarens adress. Faktum är att vi måste göra samma sak som i testet för att skicka gram.

Detta är meddelandet vi kommer att skicka till det smarta kontraktet, där msg_seqno 165 action 2 och 9.5 gram för sändning.

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

Glöm inte att signera meddelandet med din privata nyckel lottery.pk, som genererades tidigare när det smarta kontraktet skapades. Här är motsvarande åtagande.

Ta emot information från ett smart kontrakt med get-metoder

Låt oss nu titta på hur man kör smarta kontrakt få metoder.

Vi lanserar lite-client och kör get-metoderna som vi skrev.

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

В result innehåller värdet som funktionen returnerar balance() från vårt smarta kontrakt.
Vi kommer att göra detsamma för flera fler metoder.

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

Låt oss fråga efter din insatshistorik.

> 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]) ] 

Vi kommer att använda lite-klient och få metoder för att visa information om det smarta kontraktet på sajten.

Visar smart kontraktsdata på webbplatsen

Jag skrev en enkel webbplats i Python för att visa data från det smarta kontraktet på ett bekvämt sätt. Här kommer jag inte att uppehålla mig i detalj och kommer att publicera sajten i ett åtagande.

Förfrågningar till TON görs från Python via lite-client. För enkelhetens skull är webbplatsen förpackad i Docker och publicerad på Google Cloud. Länk.

Låt oss försöka

Låt oss nu försöka skicka gram dit för påfyllning från handväska. Vi skickar 40 gram. Och låt oss göra ett par satsningar för tydlighetens skull. Vi ser att sidan visar historiken för vad, den aktuella vinstprocenten och annan användbar information.

Vi seratt vi vann den första, förlorade den andra.

efterordet

Artikeln visade sig vara mycket längre än jag förväntade mig, kanske kunde den ha varit kortare, eller kanske bara för en person som inte vet något om TON och vill skriva och publicera ett inte så enkelt smart kontrakt med möjlighet att interagera med Det. Vissa saker kunde kanske ha förklarats enklare.

Kanske kunde vissa aspekter av implementeringen ha gjorts mer effektivt och elegant, men då hade det tagit ännu mer tid att förbereda artikeln. Det är också möjligt att jag gjorde ett misstag någonstans eller inte förstod något, så om du gör något allvarligt måste du lita på den officiella dokumentationen eller det officiella förrådet med TON-koden.

Det bör noteras att eftersom TON själv fortfarande är i det aktiva utvecklingsstadiet, kan förändringar inträffa som kommer att bryta något av stegen i den här artikeln (vilket hände medan jag skrev, det har redan korrigerats), men det allmänna tillvägagångssättet är kommer sannolikt inte att förändras.

Jag kommer inte att prata om framtiden för TON. Kanske kommer plattformen att bli något stort och vi borde lägga tid på att studera den och fylla en nisch med våra produkter nu.

Det finns också Libra från Facebook, som har en potentiell publik av användare större än TON. Jag vet nästan ingenting om Vågen, av forumet att döma är det mycket mer aktivitet där än i TON-communityt. Även om utvecklarna och gemenskapen av TON är mer som underground, vilket också är coolt.

referenser

  1. Officiell TON-dokumentation: https://test.ton.org
  2. Officiellt TON-förråd: https://github.com/ton-blockchain/ton
  3. Officiell plånbok för olika plattformar: https://wallet.ton.org
  4. Smart kontraktsförråd från denna artikel: https://github.com/raiym/astonished
  5. Länk till webbplatsen för smart kontrakt: https://ton-lottery.appspot.com
  6. Arkiv för tillägget för Visual Studio Code for FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Chatta om TON i Telegram, som verkligen hjälpte till att reda ut det i det inledande skedet. Jag tror att det inte kommer att vara ett misstag om jag säger att alla som skrivit något för TON är där. Du kan också be om provgram där. https://t.me/tondev_ru
  8. En annan chatt om TON där jag hittade användbar information: https://t.me/TONgramDev
  9. Första etappen av tävlingen: https://contest.com/blockchain
  10. Andra etappen av tävlingen: https://contest.com/blockchain-2

Källa: will.com

Lägg en kommentar