O tym, jak napisać i opublikować inteligentną umowę w otwartej sieci Telegram (TON)

O tym, jak napisać i opublikować inteligentny kontrakt w TON

O czym jest ten artykuł?

W artykule opowiem o tym, jak wziąłem udział w pierwszym (z dwóch) konkursie blockchain Telegramu, nie odebrałem nagrody i postanowiłem zapisać swoje doświadczenia w artykule, aby nie zapadły w zapomnienie i być może pomogły ktoś.

Ponieważ nie chciałem pisać abstrakcyjnego kodu, ale zrobić coś działającego, na potrzeby artykułu napisałem inteligentną umowę na natychmiastową loterię oraz stronę internetową, która pokazuje dane inteligentnej umowy bezpośrednio z TON bez korzystania z pamięci pośredniej.

Artykuł przyda się tym, którzy chcą zawrzeć swój pierwszy inteligentny kontrakt w TON, ale nie wiedzą od czego zacząć.

Na przykładzie loterii przejdę od instalacji środowiska do opublikowania inteligentnego kontraktu, interakcji z nim i napisania strony internetowej służącej do odbierania i publikowania danych.

O udziale w konkursie

W październiku ubiegłego roku Telegram ogłosił konkurs blockchain z nowymi językami Fift и FunC. Należało dokonać wyboru pomiędzy napisaniem któregokolwiek z pięciu proponowanych inteligentnych kontraktów. Pomyślałem, że fajnie byłoby zrobić coś innego, nauczyć się języka i coś zrobić, nawet jeśli nie będę musiał w przyszłości pisać nic innego. Poza tym temat ciągle na ustach.

Warto powiedzieć, że nie miałem doświadczenia w tworzeniu inteligentnych kontraktów.

Planowałem wziąć udział do samego końca, aż będę mógł, a potem napisać artykuł recenzyjny, ale przy pierwszym nie udało mi się. I napisał portfel z włączonym multipodpisem FunC i generalnie zadziałało. Wziąłem to za podstawę inteligentny kontrakt na Solidity.

Pomyślałem wtedy, że to zdecydowanie wystarczy, aby zająć choć część nagrody. W rezultacie około 40 z 60 uczestników zostało laureatami, a ja nie znalazłem się wśród nich. Generalnie nie ma w tym nic złego, ale jedna rzecz mnie niepokoiła. W momencie ogłoszenia wyników nie była jeszcze zrobiona recenzja testu do mojego kontraktu, pytałem uczestników na czacie, czy jest ktoś jeszcze, kto tego nie ma, nie było.

Najwyraźniej zwracając uwagę na moje wiadomości, dwa dni później sędziowie opublikowali komentarz, a ja nadal nie rozumiem, czy przypadkowo przeoczyli mój smart kontrakt w trakcie oceniania, czy po prostu pomyśleli, że jest tak źle, że nie wymaga komentarza. Zadałem pytanie na stronie, ale nie otrzymałem odpowiedzi. Chociaż nie jest tajemnicą, kto oceniał, uznałem, że pisanie osobistych wiadomości jest niepotrzebne.

Dużo czasu poświęcono na zrozumienie, dlatego postanowiono napisać artykuł. Ponieważ nie ma jeszcze zbyt wielu informacji, ten artykuł pomoże zaoszczędzić czas wszystkim zainteresowanym.

Koncepcja inteligentnych kontraktów w TON

Zanim cokolwiek napiszesz, musisz się zastanowić, od której strony podejść do tej sprawy. Dlatego teraz powiem ci, z jakich części składa się system. Dokładniej, jakie części musisz znać, aby napisać przynajmniej jakąś umowę o pracę.

Skupimy się na napisaniu inteligentnej umowy i współpracy z nią TON Virtual Machine (TVM), Fift и FunC, więc artykuł bardziej przypomina opis rozwoju zwykłego programu. Nie będziemy się tutaj rozwodzić nad tym, jak działa sama platforma.

Ogólnie o tym jak to działa TVM i język Fift istnieje dobra oficjalna dokumentacja. Uczestnicząc w konkursie, a teraz pisząc obecną umowę, często się do niej zwracałam.

Głównym językiem, w którym pisane są inteligentne kontrakty, jest FunC. W tej chwili nie ma na ten temat dokumentacji, więc żeby coś napisać trzeba przestudiować przykłady inteligentnych kontraktów z oficjalnego repozytorium i tam implementację samego języka, dodatkowo można przyjrzeć się przykładom inteligentnych kontraktów z dwóch ostatnich zawody. Linki na końcu artykułu.

Powiedzmy, że napisaliśmy już inteligentną umowę na FunC, następnie kompilujemy kod do asemblera Fift.

Skompilowana inteligentna umowa pozostaje do opublikowania. Aby to zrobić, musisz napisać funkcję w Fift, który jako dane wejściowe pobierze kod inteligentnego kontraktu i kilka innych parametrów, a wynikiem będzie plik z rozszerzeniem .boc (co oznacza „worek komórek”) oraz, w zależności od tego, jak go zapiszemy, klucz prywatny i adres, który generowany jest na podstawie inteligentnego kodu kontraktu. Można już wysyłać gramy na adres inteligentnej umowy, która nie została jeszcze opublikowana.

Aby opublikować inteligentny kontrakt w otrzymanym TON .boc plik będzie musiał zostać wysłany do blockchaina przy użyciu lekkiego klienta (więcej na ten temat poniżej). Ale przed opublikowaniem musisz przenieść gramy na wygenerowany adres, w przeciwnym razie inteligentna umowa nie zostanie opublikowana. Po opublikowaniu możesz wchodzić w interakcję z inteligentnym kontraktem, wysyłając do niego wiadomości z zewnątrz (na przykład za pomocą lekkiego klienta) lub od wewnątrz (na przykład jeden inteligentny kontrakt wysyła inny komunikat wewnątrz TON).

Gdy zrozumiemy, w jaki sposób kod jest publikowany, stanie się to łatwiejsze. Z grubsza wiemy, co chcemy napisać i jak nasz program będzie działał. A pisząc, szukamy, jak jest to już zaimplementowane w istniejących inteligentnych kontraktach, lub zaglądamy do kodu implementacyjnego Fift и FunC w oficjalnym repozytorium lub zajrzyj do oficjalnej dokumentacji.

Bardzo często szukałem słów kluczowych na czacie Telegramu, na którym gromadzili się wszyscy uczestnicy konkursu i pracownicy Telegramu, i tak się złożyło, że podczas zawodów wszyscy się tam zebrali i zaczęli dyskutować o Fift i FunC. Link na końcu artykułu.

Czas przejść od teorii do praktyki.

Przygotowanie środowiska do współpracy z TON

Zrobiłem wszystko, co zostanie opisane w artykule na MacOS i sprawdziłem to dwukrotnie w czystym Ubuntu 18.04 LTS na Dockerze.

Pierwszą rzeczą, którą musisz zrobić, to pobrać i zainstalować lite-client za pomocą którego możesz wysyłać zapytania do TON.

Instrukcje na oficjalnej stronie internetowej opisują proces instalacji dość szczegółowo i przejrzyście, pomijając niektóre szczegóły. Tutaj postępujemy zgodnie z instrukcjami, po drodze instalując brakujące zależności. Nie kompilowałem każdego projektu sam i instalowałem z oficjalnego repozytorium Ubuntu (na MacOS używałem 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 

Po zainstalowaniu wszystkich zależności możesz przystąpić do instalacji lite-client, Fift, FunC.

Najpierw klonujemy repozytorium TON wraz z jego zależnościami. Dla wygody wszystko zrobimy w folderze ~/TON.

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

W repozytorium przechowywane są także implementacje Fift и FunC.

Teraz jesteśmy gotowi do montażu projektu. Kod repozytorium jest klonowany do folderu ~/TON/ton, ~/TON utwórz folder build i zbierz w nim projekt.

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

Ponieważ mamy zamiar napisać inteligentną umowę, potrzebujemy nie tylko lite-clientAle Fift с FunC, więc skompilujmy wszystko. To nie jest szybki proces, więc czekamy.

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

Następnie pobierz plik konfiguracyjny, który zawiera dane o węźle do którego lite-client połączy się.

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

Składanie pierwszych próśb do TON

Teraz uruchommy lite-client.

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

Jeśli kompilacja przebiegła pomyślnie, po uruchomieniu zobaczysz dziennik połączenia lekkiego klienta z węzłem.

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

Możesz uruchomić polecenie help i zobacz, jakie polecenia są dostępne.

help

Wymieńmy polecenia, których będziemy używać w tym artykule.

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

Teraz jesteśmy gotowi do napisania samej umowy.

realizacja

Pomysł

Jak pisałem powyżej, smart kontrakt, który piszemy, jest loterią.

Co więcej, nie jest to loteria, w której trzeba kupić los i czekać godzinę, dzień lub miesiąc, ale loteria błyskawiczna, w której użytkownik przenosi się na adres umowy N gramów i natychmiast je odzyskuje 2 * N gramów lub traci. Prawdopodobieństwo wygranej zwiększymy na około 40%. Jeżeli nie starczy gramów na płatność, wówczas transakcję potraktujemy jako doładowanie.

Co więcej, ważne jest, aby zakłady można było oglądać w czasie rzeczywistym i w wygodnej formie, aby użytkownik mógł od razu zrozumieć, czy wygrał, czy przegrał. Dlatego musisz stworzyć stronę internetową, która będzie pokazywać zakłady i wyniki bezpośrednio z TON.

Pisanie inteligentnej umowy

Dla wygody zaznaczyłem kod FunC; wtyczkę można znaleźć i zainstalować w wyszukiwarce Visual Studio Code; jeśli nagle będziesz chciał coś dodać, udostępniłem wtyczkę publicznie. Poza tym ktoś wcześniej stworzył wtyczkę do pracy z Fiftem, możesz ją także zainstalować i znaleźć w VSC.

Stwórzmy od razu repozytorium, w którym zatwierdzimy wyniki pośrednie.

Aby ułatwić nam życie, napiszemy inteligentną umowę i będziemy ją testować lokalnie, aż będzie gotowa. Dopiero potem opublikujemy go w TON.

Inteligentna umowa ma dwie zewnętrzne metody, do których można uzyskać dostęp. Pierwszy, recv_external() funkcja ta jest wykonywana, gdy żądanie do umowy pochodzi ze świata zewnętrznego, czyli nie np. z TON, gdy sami generujemy wiadomość i wysyłamy ją przez klienta lite. Drugi, recv_internal() ma to miejsce wtedy, gdy w samej TON jakakolwiek umowa odnosi się do naszej. W obu przypadkach możesz przekazać parametry do funkcji.

Zacznijmy od prostego przykładu, który zadziała, jeśli zostanie opublikowany, ale nie ma w nim żadnego obciążenia funkcjonalnego.

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

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

Tutaj musimy wyjaśnić, o co chodzi slice. Wszystkie dane przechowywane w TON Blockchain stanowią zbiór TVM cell lub po prostu cell, w takiej komórce można przechowywać do 1023 bitów danych i do 4 łączy do innych komórek.

TVM cell slice lub slice jest to część istniejącego cell służy do jego analizy, stanie się to jasne później. Najważniejsze dla nas jest to, że możemy dokonać transferu slice i w zależności od rodzaju wiadomości przetwarzamy dane recv_external() lub recv_internal().

impure — słowo kluczowe wskazujące, że funkcja modyfikuje dane inteligentnych kontraktów.

Zapiszmy kod umowy w formacie lottery-code.fc i skompiluj.

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

Znaczenie flag można sprawdzić za pomocą polecenia

~/TON/build/crypto/func -help

Skompilowaliśmy kod asemblera Fift w 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

Można go uruchomić lokalnie, do tego przygotujemy środowisko.

Pamiętaj, że pierwsza linia łączy Asm.fif, to jest kod napisany w Fift dla asemblera Fift.

Ponieważ chcemy uruchomić i przetestować inteligentny kontrakt lokalnie, utworzymy plik lottery-test-suite.fif i skopiuj tam skompilowany kod, zastępując ostatnią linię, która zapisuje kod inteligentnego kontraktu do stałej codeaby następnie przenieść go na maszynę wirtualną:

"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

Na razie wszystko wydaje się jasne, teraz dodajmy do tego samego pliku kod, którego użyjemy do uruchomienia 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 rejestrujemy kontekst, czyli dane, z którymi zostanie uruchomiony TVM (lub stan sieci). Już w trakcie konkursu jeden z programistów pokazał, jak tworzyć c7 i skopiowałem. W tym artykule być może będziemy musieli dokonać zmian rand_seed ponieważ od tego zależy wygenerowanie liczby losowej i jeśli nie zostanie zmieniona, za każdym razem zostanie zwrócona ta sama liczba.

recv_internal и recv_external stałe o wartościach 0 i -1 będą odpowiedzialne za wywołanie odpowiednich funkcji w inteligentnym kontrakcie.

Teraz jesteśmy gotowi stworzyć pierwszy test dla naszego pustego inteligentnego kontraktu. Dla przejrzystości na razie wszystkie testy dodamy do tego samego pliku lottery-test-suite.fif.

Stwórzmy zmienną storage i wpisz w niego pusty cell, będzie to inteligentne przechowywanie kontraktów.

message To jest wiadomość, którą przekażemy inteligentnemu kontaktowi z zewnątrz. Na razie sprawimy, że będzie pusty.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Po przygotowaniu stałych i zmiennych uruchamiamy TVM za pomocą polecenia runvmctx i przekazać utworzone parametry na wejście.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

W końcu nam się to uda takie kod pośredni dla Fift.

Teraz możemy uruchomić powstały kod.

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

Program powinien działać bez błędów, a na wyjściu zobaczymy dziennik wykonania:

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

Świetnie, napisaliśmy pierwszą działającą wersję smart kontraktu.

Teraz musimy dodać funkcjonalność. Najpierw zajmijmy się wiadomościami, które przychodzą ze świata zewnętrznego recv_external()

Deweloper sam wybiera format wiadomości, jaki akceptuje umowa.

Ale zazwyczaj

  • po pierwsze chcemy zabezpieczyć naszą umowę przed światem zewnętrznym i sprawić aby tylko właściciel umowy mógł wysyłać do niej komunikaty zewnętrzne.
  • po drugie, gdy wysyłamy prawidłową wiadomość do TON, chcemy, aby stało się to dokładnie raz, a gdy wysyłamy tę samą wiadomość ponownie, inteligentny kontrakt ją odrzuca.

Zatem prawie każda umowa rozwiązuje te dwa problemy, ponieważ nasza umowa akceptuje komunikaty zewnętrzne, o to też musimy zadbać.

Zrobimy to w odwrotnej kolejności. Na początek rozwiążmy problem z powtórzeniami, jeżeli kontrakt już otrzymał taki komunikat i go przetworzył, to nie wykona go drugi raz. I wtedy rozwiążemy problem tak, aby tylko określony krąg osób mógł wysyłać wiadomości do inteligentnej umowy.

Istnieją różne sposoby rozwiązania problemu zduplikowanych wiadomości. Oto jak to zrobimy. W inteligentnym kontrakcie inicjujemy licznik odebranych wiadomości wartością początkową 0. W każdym komunikacie do inteligentnego kontraktu będziemy dopisywać aktualną wartość licznika. Jeśli wartość licznika w wiadomości nie jest zgodna z wartością w inteligentnym kontrakcie, to go nie przetwarzamy; jeśli tak, to przetwarzamy i zwiększamy licznik w inteligentnym kontrakcie o 1.

Wracamy do lottery-test-suite.fif i dodaj do niego drugi test. Jeśli wyślemy błędny numer, kod powinien zgłosić wyjątek. Przykładowo, niech dane kontraktu przechowują 166, a my wyślemy 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"

Uruchommy.

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

Zobaczymy, że test zostanie wykonany z błędem.

[ 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

Na tym etapie lottery-test-suite.fif powinno wyglądać по ссылке.

Dodajmy teraz logikę licznika do inteligentnej umowy 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 leży wiadomość, którą wysyłamy.

Pierwszą rzeczą, którą robimy, jest sprawdzenie, czy wiadomość zawiera dane, jeśli nie, to po prostu wychodzimy.

Następnie analizujemy wiadomość. in_msg~load_uint(32) ładuje liczbę 165, 32-bitową unsigned int z przesłanej wiadomości.

Następnie ładujemy 32 bity z pamięci inteligentnych kontraktów. Sprawdzamy, czy załadowana liczba jest zgodna z przekazaną; jeśli nie, zgłaszamy wyjątek. W naszym przypadku, ponieważ przekazujemy niedopasowanie, należy zgłosić wyjątek.

Teraz skompilujmy.

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

Skopiuj wynikowy kod do lottery-test-suite.fif, nie zapominając o zastąpieniu ostatniej linii.

Sprawdzamy, czy test przeszedł pomyślnie:

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

Tutaj Możesz zobaczyć odpowiednie zatwierdzenie z bieżącymi wynikami.

Zauważ, że niewygodne jest ciągłe kopiowanie skompilowanego kodu inteligentnego kontraktu do pliku z testami, dlatego napiszemy skrypt, który zapisze nam kod na stałą, a skompilowany kod po prostu połączymy z naszymi testami za pomocą "include".

Utwórz plik w folderze projektu build.sh z następującą treścią.

#!/bin/bash

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

Zróbmy to wykonalnym.

chmod +x ./build.sh

Teraz wystarczy uruchomić nasz skrypt, aby skompilować umowę. Ale poza tym musimy zapisać to w stałej code. Dlatego utworzymy nowy plik lotter-compiled-for-test.fif, które umieścimy w pliku lottery-test-suite.fif.

Dodajmy kod spódnicy do sh, który po prostu zduplikuje skompilowany plik lotter-compiled-for-test.fif i zmień w nim ostatnią linię.

# 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

Teraz dla sprawdzenia uruchommy powstały skrypt i zostanie wygenerowany plik lottery-compiled-for-test.fif, które umieścimy w naszym lottery-test-suite.fif

В lottery-test-suite.fif usuń kod umowy i dodaj linię "lottery-compiled-for-test.fif" include.

Przeprowadzamy testy, aby sprawdzić, czy przechodzą pomyślnie.

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

Świetnie, teraz aby zautomatyzować uruchamianie testów utwórzmy plik test.sh, który zostanie wykonany jako pierwszy build.sh, a następnie uruchom testy.

touch test.sh
chmod +x test.sh

Piszemy w środku

./build.sh 

echo "nCompilation completedn"

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

Zróbmy to test.sh i uruchom go, aby upewnić się, że testy działają.

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

Sprawdzamy, czy umowa się skompilowała, a testy zostały wykonane.

Świetnie, teraz podczas uruchamiania test.sh Testy zostaną skompilowane i uruchomione natychmiast. Oto link do popełniać.

OK, zanim będziemy kontynuować, zróbmy jeszcze jedną rzecz dla wygody.

Stwórzmy folder build gdzie będziemy przechowywać skopiowany kontrakt i jego klon zapisany w stałej lottery-compiled.fif, lottery-compiled-for-test.fif. Utwórzmy także folder test gdzie będzie przechowywany plik testowy? lottery-test-suite.fif i potencjalnie inne pliki pomocnicze. Link do odpowiednich zmian.

Kontynuujmy rozwój inteligentnej umowy.

Następnie powinien nastąpić test sprawdzający czy wiadomość dotarła i licznik w sklepie jest aktualizowany w momencie wysłania prawidłowego numeru. Ale zrobimy to później.

Zastanówmy się teraz, jaka struktura danych i jakie dane muszą być przechowywane w inteligentnym kontrakcie.

Opiszę wszystko co przechowujemy.

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

Następnie musisz napisać dwie funkcje. Zadzwonimy do pierwszego pack_state(), który spakuje dane do późniejszego zapisania w inteligentnym magazynie kontraktów. Nazwijmy to drugie unpack_state() odczyta i zwróci dane z pamięci.

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

Te dwie funkcje dodajemy na początku inteligentnej umowy. Ułóży się takie wynik pośredni.

Aby zapisać dane, musisz wywołać wbudowaną funkcję set_data() i będzie zapisywał dane z pack_state() w inteligentnym magazynie kontraktów.

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

Skoro już mamy wygodne funkcje zapisu i odczytu danych, możemy przejść dalej.

Musimy sprawdzić, czy wiadomość przychodząca z zewnątrz jest podpisana przez właściciela umowy (lub innego użytkownika, który ma dostęp do klucza prywatnego).

Kiedy publikujemy inteligentną umowę, możemy ją zainicjować potrzebnymi danymi w pamięci, które zostaną zapisane do wykorzystania w przyszłości. Zapiszemy tam klucz publiczny, abyśmy mogli zweryfikować, czy przychodząca wiadomość została podpisana odpowiednim kluczem prywatnym.

Zanim przejdziemy dalej, utwórzmy klucz prywatny i zapiszmy go test/keys/owner.pk. Aby to zrobić, uruchommy Fift w trybie interaktywnym i wykonajmy cztery polecenia.

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

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

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

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

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

Stwórzmy folder keys wewnątrz folderu test i wpisz tam klucz prywatny.

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

Widzimy plik w bieżącym folderze owner.pk.

Usuwamy klucz publiczny ze stosu i w razie potrzeby możemy go pobrać z prywatnego.

Teraz musimy napisać weryfikację podpisu. Zacznijmy od testu. Najpierw odczytujemy klucz prywatny z pliku za pomocą funkcji file>B i zapisz go do zmiennej owner_private_key, a następnie za pomocą funkcji priv>pub przekonwertuj klucz prywatny na klucz publiczny i zapisz wynik 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 !

Będziemy potrzebować obu kluczy.

Inicjujemy przechowywanie inteligentnych kontraktów dowolnymi danymi w tej samej kolejności, co w funkcji pack_state()i zapisz to w zmiennej 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 !

Następnie utworzymy podpisaną wiadomość, będzie ona zawierać tylko podpis i wartość licznika.

Najpierw tworzymy dane, które chcemy przesłać, następnie podpisujemy je kluczem prywatnym i na koniec generujemy podpisaną wiadomość.

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 !  

Dzięki temu wiadomość, którą wyślemy do smart kontraktu, zostanie zapisana w zmiennej message_to_send, o funkcjach hashu, ed25519_sign_uint Możesz czytać w dokumentacji Fifta.

Aby przeprowadzić test, dzwonimy ponownie.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Tutaj tak Plik z testami powinien na tym etapie wyglądać tak.

Uruchommy test, który zakończy się niepowodzeniem, dlatego zmienimy inteligentną umowę tak, aby mogła odbierać wiadomości w tym formacie i weryfikować podpis.

Najpierw liczymy 512 bitów podpisu z wiadomości i zapisujemy go do zmiennej, następnie liczymy 32 bity zmiennej licznika.

Skoro mamy funkcję odczytu danych z magazynu inteligentnych kontraktów, to będziemy z niej korzystać.

Następnie sprawdzamy licznik przekazany wraz z magazynem i sprawdzamy podpis. Jeżeli coś nie pasuje to zgłaszamy wyjątek z odpowiednim kodem.

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

Odpowiednie zatwierdzenie tutaj.

Uruchommy testy i zobaczmy, że drugi test zakończy się niepowodzeniem. Z dwóch powodów wiadomość nie zawiera wystarczającej liczby bitów i nie ma wystarczającej liczby bitów w pamięci, więc kod ulega awarii podczas analizowania. Musimy dodać podpis do wysyłanej wiadomości i skopiować pamięć z ostatniego testu.

W drugim teście dodamy podpis wiadomości i zmienimy sposób przechowywania inteligentnych kontraktów. Tutaj tak plik z testami wygląda w tej chwili.

Napiszmy czwarty test, w którym wyślemy wiadomość podpisaną cudzym kluczem prywatnym. Stwórzmy kolejny klucz prywatny i zapiszmy go w pliku not-owner.pk. Tym kluczem prywatnym podpiszemy wiadomość. Uruchommy testy i upewnijmy się, że wszystkie testy poszły pomyślnie. Popełniać w tej chwili.

Teraz możemy w końcu przejść do wdrożenia logiki inteligentnych kontraktów.
В recv_external() przyjmiemy dwa rodzaje wiadomości.

Ponieważ nasza umowa będzie kumulować straty graczy, pieniądze te muszą zostać przekazane twórcy loterii. Adres portfela twórcy loterii jest zapisywany w pamięci podczas tworzenia umowy.

Na wszelki wypadek potrzebujemy możliwości zmiany adresu, na który wysyłamy gramy przegranych. Powinniśmy także móc wysłać gramy z loterii na adres właściciela.

Zacznijmy od pierwszego. Napiszmy najpierw test, który sprawdzi, czy po wysłaniu wiadomości inteligentny kontrakt zapisał nowy adres w pamięci. Informujemy, że w wiadomości oprócz licznika i nowego adresu przekazujemy również action 7-bitowa liczba całkowita nieujemna, w zależności od niej wybierzemy sposób przetwarzania wiadomości w inteligentnym kontrakcie.

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

W teście można zobaczyć, w jaki sposób deserializowana jest pamięć smartcontract storage w Piątce. Deserializacja zmiennych jest opisana w dokumentacji Fift.

Zatwierdź łącze z dodatkiem ciasta.

Przeprowadźmy test i upewnijmy się, że się nie powiedzie. Dodajmy teraz logikę zmiany adresu właściciela loterii.

W inteligentnej umowie kontynuujemy analizę message, wczytaj się action. Przypomnijmy, że będziemy mieli dwa action: zmień adres i wyślij gramy.

Następnie odczytujemy nowy adres właściciela umowy i zapisujemy go w pamięci.
Przeprowadzamy testy i widzimy, że trzeci test kończy się niepowodzeniem. Awaria wynika z faktu, że kontrakt teraz dodatkowo analizuje 7 bitów wiadomości, których brakuje w teście. Dodaj nieistniejącą wiadomość do wiadomości action. Przeprowadźmy testy i zobaczmy, czy wszystko przebiegło pomyślnie. Tutaj zobowiązać się do zmian. Świetnie.

Napiszmy teraz logikę wysłania określonej ilości gramów na wcześniej zapisany adres.

Najpierw napiszmy test. Napiszemy dwa testy, jeden gdy salda nie będzie wystarczającego, drugi gdy wszystko powinno przejść pomyślnie. Testy można obejrzeć w tym zatwierdzeniu.

Teraz dodajmy kod. Najpierw napiszmy dwie metody pomocnicze. Pierwszą metodą pobierania jest sprawdzenie aktualnego salda inteligentnego kontraktu.

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

Drugi służy do wysyłania gramów do innego inteligentnego kontraktu. Całkowicie skopiowałem tę metodę z innego inteligentnego kontraktu.

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

Dodajmy te dwie metody do inteligentnej umowy i napiszmy logikę. Najpierw analizujemy liczbę gramów z wiadomości. Następnie sprawdzamy saldo, jeżeli nie wystarczy rzucamy wyjątek. Jeżeli wszystko jest w porządku to wysyłamy gramy na zapisany adres i aktualizujemy licznik.

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

Tutaj tak wygląda obecnie na inteligentny kontrakt. Przeprowadźmy testy i upewnijmy się, że przeszły pomyślnie.

Nawiasem mówiąc, prowizja jest pobierana z inteligentnej umowy za każdym razem za przetworzoną wiadomość. Aby wiadomości inteligentnej umowy mogły wykonać żądanie, po podstawowych kontrolach należy zadzwonić accept_message().

Przejdźmy teraz do komunikatów wewnętrznych. W rzeczywistości przyjmujemy tylko gramy i odsyłamy graczowi podwójną kwotę, jeśli wygra, i jedną trzecią właścicielowi, jeśli przegra.

Najpierw napiszmy prosty test. Do tego potrzebny jest nam adres testowy smart kontraktu, z którego rzekomo wysyłamy gramy do smart kontraktu.

Adres inteligentnego kontraktu składa się z dwóch liczb, 32-bitowej liczby całkowitej odpowiedzialnej za łańcuch roboczy i 256-bitowej nieujemnej liczby całkowitej, unikalnego numeru konta w tym łańcuchu roboczym. Na przykład -1 i 12345, jest to adres, który zapiszemy do pliku.

Skopiowałem funkcję zapisywania adresu z 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

Przyjrzyjmy się, jak działa ta funkcja, pozwoli to zrozumieć, jak działa Fift. Uruchom Fift w trybie interaktywnym.

~/TON/build/crypto/fift -i 

Najpierw wypychamy na stos -1, 12345 i nazwę przyszłego pliku "sender.addr":

-1 12345 "sender.addr" 

Następnym krokiem jest wykonanie funkcji -rot, który przesuwa stos w taki sposób, że na górze stosu znajduje się unikalny numer inteligentnego kontraktu:

"sender.addr" -1 12345

256 u>B konwertuje 256-bitową nieujemną liczbę całkowitą na bajty.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap zamienia dwa górne elementy stosu.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B konwertuje 32-bitową liczbę całkowitą na bajty.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ łączy dwa ciągi bajtów.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Znowu swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Na koniec bajty są zapisywane do pliku B>file. Po tym nasz stos jest pusty. Zatrzymaliśmy się Fift. W bieżącym folderze utworzono plik sender.addr. Przenosimy plik do utworzonego folderu test/addresses/.

Napiszmy prosty test, który prześle gramy do inteligentnego kontraktu. Oto zatwierdzenie.

Przyjrzyjmy się teraz logice loterii.

Pierwszą rzeczą, którą robimy, jest sprawdzenie wiadomości bounced lub nie, jeśli bounced, wtedy to ignorujemy. bounced oznacza, że ​​kontrakt zwróci gramy, jeśli wystąpi jakiś błąd. Nie zwrócimy gramów, jeśli nagle wystąpi błąd.

Sprawdzamy, czy saldo jest mniejsze niż pół grama, po prostu akceptujemy wiadomość i ignorujemy ją.

Następnie analizujemy adres inteligentnej umowy, z której nadeszła wiadomość.

Odczytujemy dane z magazynu, a następnie usuwamy z historii stare zakłady, jeśli jest ich więcej niż dwadzieścia. Dla wygody napisałem trzy dodatkowe funkcje pack_order(), unpack_order(), remove_old_orders().

Następnie sprawdzamy, czy saldo nie wystarczy na płatność, następnie uważamy, że nie jest to zakład, ale uzupełnienie i zapisujemy uzupełnienie w orders.

I wreszcie istota inteligentnego kontraktu.

Po pierwsze, jeśli gracz przegra, zapisujemy to w historii zakładów, a jeśli kwota jest większa niż 3 gramy, wysyłamy 1/3 właścicielowi inteligentnej umowy.

Jeśli gracz wygra, to wysyłamy podwójną kwotę na adres gracza, a następnie zapisujemy informację o zakładzie w historii.

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

To jest to. Odpowiednie zatwierdzenie.

Teraz wszystko pozostaje proste, stwórzmy metody get, abyśmy mogli uzyskać informacje o stanie kontraktu ze świata zewnętrznego (a właściwie odczytać dane z ich inteligentnego magazynu kontraktów).

Dodajmy metody get. O tym jak otrzymać informację o inteligentnym kontrakcie napiszemy poniżej.

Zapomniałem też dodać kodu, który przetworzy pierwsze żądanie, które pojawi się podczas publikowania inteligentnej umowy. Odpowiednie zatwierdzenie. I dalej poprawione błąd z wysłaniem 1/3 kwoty na konto właściciela.

Kolejnym krokiem jest opublikowanie inteligentnej umowy. Stwórzmy folder requests.

Jako podstawę wziąłem kod publikacji simple-wallet-code.fc który może znaleźć w oficjalnym repozytorium.

Coś na co warto zwrócić uwagę. Generujemy inteligentny magazyn kontraktów i komunikat wejściowy. Następnie generowany jest adres inteligentnej umowy, czyli adres jest znany jeszcze przed publikacją w TON. Następnie musisz wysłać kilka gramów na ten adres, a dopiero potem wysłać plik z samym inteligentnym kontraktem, ponieważ sieć pobiera prowizję za przechowywanie inteligentnego kontraktu i operacje na nim (walidatorzy, którzy przechowują i wykonują inteligentne kontrakty umowy). Kod można zobaczyć tutaj.

Następnie wykonujemy kod publikowania i otrzymujemy lottery-query.boc plik i adres inteligentnej umowy.

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

Nie zapomnij zapisać wygenerowanych plików: lottery-query.boc, lottery.addr, lottery.pk.

W logach wykonania zobaczymy między innymi adres inteligentnej umowy.

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

Dla zabawy złóżmy prośbę do TON

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

I zobaczymy, że konto o tym adresie jest puste.

account state is empty

Wysyłamy pod wskazany adres 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram i po kilku sekundach wykonujemy to samo polecenie. Aby wysłać gramy, używam oficjalny portfeli możesz poprosić kogoś z czatu o gramy testowe, o czym opowiem na końcu artykułu.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Wygląda na niezainicjowany (state:account_uninit) inteligentny kontrakt z tym samym adresem i saldem 1 000 000 000 nanogramów.

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

Teraz opublikujmy inteligentną umowę. Uruchommy klienta Lite i wykonajmy.

> 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 

Sprawdźmy, czy umowa została opublikowana.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Między innymi dostajemy.

  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

Widzimy to account_active.

Odpowiednie zatwierdzenie ze zmianami tutaj.

Utwórzmy teraz żądania interakcji z inteligentną umową.

Dokładniej, pierwszą czynność dotyczącą zmiany adresu pozostawimy jako samodzielną pracę, a drugą wykonamy w celu przesłania gramów na adres właściciela. W rzeczywistości będziemy musieli zrobić to samo, co w teście na wysyłanie gramów.

To jest wiadomość, którą wyślemy do inteligentnej umowy, gdzie msg_seqno 165, action 2 i 9.5 grama do wysłania.

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

Nie zapomnij podpisać wiadomości swoim kluczem prywatnym lottery.pk, który został wygenerowany wcześniej podczas tworzenia inteligentnej umowy. Oto odpowiednie zatwierdzenie.

Odbieranie informacji z inteligentnego kontraktu za pomocą metod get

Przyjrzyjmy się teraz, jak uruchomić metody pobierania inteligentnych kontraktów.

Biegnij lite-client i uruchom metody get, które napisaliśmy.

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

В result zawiera wartość zwracaną przez funkcję balance() z naszego inteligentnego kontraktu.
Zrobimy to samo dla kilku kolejnych metod.

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

Poprośmy o historię zakładów.

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

Wykorzystamy klienta Lite i uzyskamy metody wyświetlania informacji o inteligentnej umowie na stronie.

Wyświetlanie danych inteligentnych kontraktów na stronie internetowej

Napisałem prostą stronę internetową w Pythonie, aby w wygodny sposób wyświetlić dane z inteligentnej umowy. Tutaj nie będę się nad tym szczegółowo rozwodzić i opublikuję stronę w jednym zatwierdzeniu.

Prośby do TON są wysyłane z Python przez lite-client. Dla wygody strona jest spakowana w Dockerze i opublikowana w Google Cloud. Połączyć.

Spróbujmy

Teraz spróbujmy wysłać tam gramy w celu uzupełnienia portfel. Wyślemy 40 gramów. I dla jasności zróbmy kilka zakładów. Widzimy, że strona pokazuje historię zakładów, aktualny procent wygranej i inne przydatne informacje.

Widzimyże wygraliśmy pierwszy, przegraliśmy drugi.

Posłowie

Artykuł okazał się znacznie dłuższy niż się spodziewałem, może mógł być krótszy, a może tylko dla osoby, która nie ma pojęcia o TON, a chce napisać i opublikować nie taki prosty inteligentny kontrakt z możliwością interakcji z To. Być może pewne rzeczy można było wyjaśnić prościej.

Być może pewne aspekty wdrożenia można było wykonać sprawniej i elegancko, ale wówczas przygotowanie artykułu zajęłoby jeszcze więcej czasu. Możliwe też, że gdzieś popełniłem błąd lub czegoś nie zrozumiałem, więc jeśli robisz coś poważnego, musisz zdać się na oficjalną dokumentację lub oficjalne repozytorium z kodem TON.

Należy zauważyć, że ponieważ sam TON jest wciąż w aktywnej fazie rozwoju, mogą wystąpić zmiany, które spowodują przerwanie któregokolwiek z etapów tego artykułu (co miało miejsce, gdy pisałem, zostało to już poprawione), ale ogólne podejście jest takie raczej się nie zmieni.

Nie będę mówił o przyszłości TON. Być może platforma stanie się czymś dużym i powinniśmy poświęcić czas na jej przestudiowanie i już teraz wypełnić niszę naszymi produktami.

Jest też Libra z Facebooka, która ma potencjalną widownię użytkowników większą niż TON. O Wadze nie wiem prawie nic, sądząc po forum, jest tam znacznie większa aktywność niż w społeczności TON. Chociaż twórcy i społeczność TON bardziej przypominają underground, co też jest fajne.

referencje

  1. Oficjalna dokumentacja TON: https://test.ton.org
  2. Oficjalne repozytorium TON: https://github.com/ton-blockchain/ton
  3. Oficjalny portfel na różne platformy: https://wallet.ton.org
  4. Repozytorium inteligentnych kontraktów z tego artykułu: https://github.com/raiym/astonished
  5. Link do strony internetowej inteligentnej umowy: https://ton-lottery.appspot.com
  6. Repozytorium rozszerzenia dla Visual Studio Code for FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Porozmawiaj o TON w Telegramie, co naprawdę pomogło to rozgryźć na początkowym etapie. Myślę, że nie będzie błędem, jeśli powiem, że są tam wszyscy, którzy napisali coś dla TON. Tam też możesz poprosić o gramy testowe. https://t.me/tondev_ru
  8. Kolejna rozmowa o TON, w której znalazłem przydatne informacje: https://t.me/TONgramDev
  9. Pierwszy etap konkursu: https://contest.com/blockchain
  10. Drugi etap konkursu: https://contest.com/blockchain-2

Źródło: www.habr.com

Dodaj komentarz