Tentang cara menulis dan mempublikasikan kontrak pintar di Telegram Open Network (TON)

Tentang cara menulis dan menerbitkan kontrak pintar di TON

Tentang apa artikel ini?

Dalam artikel ini saya akan berbicara tentang bagaimana saya mengikuti (dari dua) kompetisi blockchain Telegram yang pertama, tidak mengambil hadiah, dan memutuskan untuk mencatat pengalaman saya dalam sebuah artikel agar tidak terlupakan dan, mungkin, membantu seseorang.

Karena saya tidak ingin menulis kode abstrak, tetapi untuk melakukan sesuatu yang berfungsi, untuk artikel tersebut saya menulis kontrak pintar untuk lotere instan dan situs web yang menampilkan data kontrak pintar langsung dari TON tanpa menggunakan penyimpanan perantara.

Artikel ini akan berguna bagi mereka yang ingin membuat kontrak pintar pertama mereka di TON, namun tidak tahu harus mulai dari mana.

Dengan menggunakan lotere sebagai contoh, saya akan mulai dari menginstal lingkungan hingga menerbitkan kontrak pintar, berinteraksi dengannya, dan menulis situs web untuk menerima dan menerbitkan data.

Tentang partisipasi dalam kompetisi

Oktober lalu, Telegram mengumumkan kompetisi blockchain dengan bahasa baru Fift ΠΈ FunC. Penting untuk memilih salah satu dari lima kontrak pintar yang diusulkan. Saya pikir akan menyenangkan untuk melakukan sesuatu yang berbeda, belajar bahasa dan membuat sesuatu, bahkan jika saya tidak perlu menulis apa pun lagi di masa depan. Ditambah lagi, topiknya terus-menerus menjadi perbincangan.

Patut dikatakan bahwa saya tidak memiliki pengalaman mengembangkan kontrak pintar.

Saya berencana untuk berpartisipasi sampai akhir sampai saya bisa dan kemudian menulis artikel ulasan, tetapi saya langsung gagal pada artikel pertama. SAYA tulis dompet dengan multi-tanda tangan aktif FunC dan secara umum berhasil. Saya menganggapnya sebagai dasar kontrak pintar pada Soliditas.

Pada saat itu, saya berpikir bahwa ini sudah cukup untuk mengambil setidaknya beberapa tempat hadiah. Hasilnya, sekitar 40 dari 60 peserta menjadi pemenang dan saya tidak termasuk di antara mereka. Secara umum, tidak ada yang salah dengan hal ini, tetapi ada satu hal yang mengganggu saya. Pada saat pengumuman hasil, review tes kontrak saya belum dilakukan, saya bertanya kepada peserta chat apakah ada orang lain yang belum punya, tidak ada.

Rupanya memperhatikan pesan saya, dua hari kemudian para juri menerbitkan komentar dan saya masih tidak mengerti apakah mereka secara tidak sengaja melewatkan kontrak pintar saya selama penjurian atau hanya berpikir bahwa itu sangat buruk sehingga tidak memerlukan komentar. Saya mengajukan pertanyaan di halaman tersebut, tetapi tidak menerima jawaban. Meski bukan rahasia lagi siapa yang menilai, saya menilai tidak perlu menulis pesan pribadi.

Banyak waktu dihabiskan untuk memahami, jadi diputuskan untuk menulis artikel. Karena informasinya belum banyak, artikel ini akan membantu menghemat waktu bagi semua orang yang tertarik.

Konsep kontrak pintar di TON

Sebelum Anda menulis apa pun, Anda perlu mencari tahu dari sisi mana Anda harus mendekati hal ini. Oleh karena itu, sekarang saya akan memberi tahu Anda apa saja bagian-bagian dari sistem itu. Lebih tepatnya, bagian apa saja yang perlu Anda ketahui untuk menulis setidaknya semacam kontrak kerja.

Kami akan fokus menulis kontrak pintar dan bekerja dengannya TON Virtual Machine (TVM), Fift ΠΈ FunC, jadi artikelnya lebih seperti gambaran perkembangan program reguler. Kami tidak akan membahas cara kerja platform itu sendiri di sini.

Secara umum tentang cara kerjanya TVM dan bahasa Fift ada dokumentasi resmi yang bagus. Saat mengikuti kompetisi dan sekarang saat menulis kontrak saat ini, saya sering menoleh padanya.

Bahasa utama yang digunakan untuk menulis kontrak pintar adalah FunC. Tidak ada dokumentasi mengenai hal itu saat ini, jadi untuk menulis sesuatu, Anda perlu mempelajari contoh kontrak pintar dari repositori resmi dan penerapan bahasa itu sendiri di sana, ditambah lagi Anda dapat melihat contoh kontrak pintar dari dua masa lalu. kompetisi. Tautan di akhir artikel.

Katakanlah kita telah menulis kontrak pintar untuknya FunC, setelah itu kita kompilasi kodenya ke dalam assembler Fift.

Kontrak pintar yang dikompilasi masih harus dipublikasikan. Untuk melakukan ini, Anda perlu menulis suatu fungsi Fift, yang akan mengambil kode kontrak pintar dan beberapa parameter lainnya sebagai masukan, dan keluarannya akan berupa file dengan ekstensi .boc (yang berarti β€œkantong sel”), dan, bergantung pada cara kita menulisnya, kunci pribadi dan alamat, yang dihasilkan berdasarkan kode kontrak pintar. Anda sudah dapat mengirimkan gram ke alamat kontrak pintar yang belum dipublikasikan.

Untuk mempublikasikan kontrak pintar dalam TON diterima .boc file tersebut perlu dikirim ke blockchain menggunakan klien ringan (lebih lanjut tentang itu di bawah). Namun sebelum dipublikasikan, Anda perlu mentransfer gram ke alamat yang dihasilkan, jika tidak, kontrak pintar tidak akan dipublikasikan. Setelah publikasi, Anda dapat berinteraksi dengan kontrak pintar dengan mengirimkan pesan dari luar (misalnya, menggunakan klien ringan) atau dari dalam (misalnya, satu kontrak pintar mengirimkan pesan lain di dalam TON).

Setelah kami memahami cara kode dipublikasikan, segalanya menjadi lebih mudah. Kami secara kasar mengetahui apa yang ingin kami tulis dan bagaimana program kami akan bekerja. Dan saat menulis, kami mencari bagaimana hal ini sudah diterapkan dalam kontrak pintar yang ada, atau kami melihat kode implementasinya Fift ΠΈ FunC di repositori resmi, atau lihat di dokumentasi resmi.

Sering sekali saya mencari kata kunci di chat Telegram tempat berkumpulnya seluruh peserta kompetisi dan karyawan Telegram, dan kebetulan saat kompetisi semua orang berkumpul disana dan mulai berdiskusi tentang Fift dan FunC. Tautan di akhir artikel.

Saatnya beralih dari teori ke praktik.

Mempersiapkan lingkungan untuk bekerja dengan TON

Saya melakukan semua yang akan dijelaskan dalam artikel di MacOS dan memeriksa ulang di Ubuntu 18.04 LTS yang bersih di Docker.

Hal pertama yang perlu Anda lakukan adalah mengunduh dan menginstal lite-client yang dengannya Anda dapat mengirim permintaan ke TON.

Petunjuk di situs resminya menjelaskan proses instalasi dengan cukup detail dan jelas serta menghilangkan beberapa detail. Di sini kita mengikuti instruksinya, menginstal dependensi yang hilang di sepanjang jalan. Saya tidak mengkompilasi sendiri setiap proyek dan menginstalnya dari repositori resmi Ubuntu (di MacOS yang saya gunakan 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 

Setelah semua dependensi terinstal, Anda dapat menginstal lite-client, Fift, FunC.

Pertama, kita mengkloning repositori TON beserta dependensinya. Untuk kenyamanan, kami akan melakukan semuanya dalam satu folder ~/TON.

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

Repositori juga menyimpan implementasi Fift ΠΈ FunC.

Sekarang kami siap untuk merakit proyek. Kode repositori diklon ke dalam folder ~/TON/ton. Di ~/TON membuat folder build dan kumpulkan proyek di dalamnya.

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

Karena kita akan menulis kontrak pintar, kita tidak hanya memerlukannya lite-clienttapi Fift с FunC, jadi mari kita kompilasi semuanya. Ini bukan proses yang cepat, jadi kami menunggu.

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

Selanjutnya download file konfigurasi yang berisi data tentang node yang dituju lite-client akan terhubung.

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

Membuat permintaan pertama ke TON

Sekarang mari kita luncurkan lite-client.

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

Jika build berhasil, maka setelah peluncuran Anda akan melihat log koneksi klien ringan ke node.

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

Anda dapat menjalankan perintah help dan lihat perintah apa yang tersedia.

help

Mari daftar perintah yang akan kita gunakan dalam artikel ini.

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-ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ смартконтракта. 

Sekarang kami siap untuk menulis kontraknya sendiri.

Implementasi

Ide

Seperti yang saya tulis di atas, kontrak pintar yang kami tulis adalah lotere.

Selain itu, ini bukan lotere di mana Anda perlu membeli tiket dan menunggu satu jam, hari atau bulan, tetapi lotere instan di mana pengguna mentransfer ke alamat kontrak N gram, dan langsung mendapatkannya kembali 2 * N gram atau hilang. Kami akan membuat kemungkinan menang sekitar 40%. Jika gram untuk pembayaran tidak mencukupi, maka transaksi tersebut akan kami anggap sebagai top-up.

Selain itu, penting agar taruhan dapat dilihat secara real time dan dalam bentuk yang nyaman, sehingga pengguna dapat segera mengetahui apakah ia menang atau kalah. Oleh karena itu, Anda perlu membuat website yang menampilkan taruhan dan hasil langsung dari TON.

Menulis kontrak cerdas

Untuk kenyamanan, saya telah menyoroti kode untuk FunC; plugin dapat ditemukan dan diinstal dalam pencarian Visual Studio Code; jika Anda tiba-tiba ingin menambahkan sesuatu, saya telah membuat plugin tersedia untuk umum. Selain itu, seseorang sebelumnya membuat plugin untuk bekerja dengan Fift, Anda juga dapat menginstalnya dan menemukannya di VSC.

Mari segera buat repositori tempat kita akan mengkomit hasil antara.

Untuk mempermudah hidup kami, kami akan menulis kontrak pintar dan mengujinya secara lokal hingga siap. Baru setelah itu kami akan mempublikasikannya di TON.

Kontrak pintar memiliki dua metode eksternal yang dapat diakses. Pertama, recv_external() fungsi ini dijalankan ketika permintaan kontrak datang dari dunia luar, yaitu bukan dari TON, misalnya ketika kita sendiri yang membuat pesan dan mengirimkannya melalui klien ringan. Kedua, recv_internal() ini adalah saat, di dalam TON sendiri, kontrak apa pun mengacu pada kontrak kami. Dalam kedua kasus tersebut, Anda dapat meneruskan parameter ke fungsi.

Mari kita mulai dengan contoh sederhana yang akan berfungsi jika dipublikasikan, tetapi tidak ada beban fungsional di dalamnya.

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

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

Di sini kita perlu menjelaskan apa itu slice. Semua data yang disimpan di TON Blockchain adalah koleksi TVM cell atau hanya cell, dalam sel seperti itu Anda dapat menyimpan hingga 1023 bit data dan hingga 4 tautan ke sel lain.

TVM cell slice ΠΈΠ»ΠΈ slice ini adalah bagian dari yang sudah ada cell digunakan untuk menguraikannya, nanti akan menjadi jelas. Hal utama bagi kami adalah kami bisa mentransfer slice dan bergantung pada jenis pesan, proses data masuk recv_external() ΠΈΠ»ΠΈ recv_internal().

impure β€” kata kunci yang menunjukkan bahwa fungsi tersebut mengubah data kontrak pintar.

Mari kita simpan kode kontraknya lottery-code.fc dan kompilasi.

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

Arti dari bendera dapat dilihat menggunakan perintah

~/TON/build/crypto/func -help

Kami telah mengkompilasi kode assembler Fift 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

Bisa diluncurkan secara lokal, untuk itu kita akan siapkan lingkungannya.

Perhatikan bahwa baris pertama terhubung Asm.fif, ini adalah kode yang ditulis dalam Fift untuk assembler Fift.

Karena kami ingin menjalankan dan menguji kontrak pintar secara lokal, kami akan membuat file lottery-test-suite.fif dan salin kode yang dikompilasi di sana, ganti baris terakhir di dalamnya, yang menulis kode kontrak pintar ke sebuah konstanta codeuntuk kemudian mentransfernya ke mesin virtual:

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

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

Sejauh ini tampak jelas, sekarang mari tambahkan kode yang akan kita gunakan untuk meluncurkan TVM ke file yang sama.

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 kami mencatat konteksnya, yaitu data yang akan digunakan untuk meluncurkan TVM (atau status jaringan). Bahkan saat kompetisi, salah satu developer menunjukkan cara berkreasi c7 dan saya menyalin. Dalam artikel ini kita mungkin perlu berubah rand_seed karena pembuatan nomor acak bergantung padanya dan jika tidak diubah, nomor yang sama akan dikembalikan setiap saat.

recv_internal ΠΈ recv_external konstanta dengan nilai 0 dan -1 akan bertanggung jawab untuk memanggil fungsi terkait dalam kontrak pintar.

Sekarang kami siap membuat pengujian pertama untuk kontrak pintar kami yang kosong. Untuk lebih jelasnya, untuk saat ini kami akan menambahkan semua pengujian ke file yang sama lottery-test-suite.fif.

Mari buat variabel storage dan tulis yang kosong ke dalamnya cell, ini akan menjadi penyimpanan kontrak pintar.

message Ini adalah pesan yang akan kami kirimkan ke kontak pintar dari luar. Kami juga akan mengosongkannya untuk saat ini.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

Setelah kami menyiapkan konstanta dan variabel, kami meluncurkan TVM menggunakan perintah runvmctx dan meneruskan parameter yang dibuat ke input.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

Pada akhirnya kita akan berhasil seperti itu kode perantara untuk Fift.

Sekarang kita dapat menjalankan kode yang dihasilkan.

export FIFTPATH=~/TON/ton/crypto/fift/lib // выполняСм ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π· для удобства 
~/TON/build/crypto/fift -s lottery-test-suite.fif 

Program harus berjalan tanpa kesalahan dan pada output kita akan melihat log eksekusi:

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

Hebat, kami telah menulis versi pertama kontrak pintar yang berfungsi.

Sekarang kita perlu menambahkan fungsionalitas. Pertama mari kita berurusan dengan pesan-pesan yang datang dari dunia luar recv_external()

Pengembang sendiri yang memilih format pesan yang dapat diterima kontrak.

Tapi biasanya

  • pertama, kami ingin melindungi kontrak kami dari dunia luar dan membuat hanya pemilik kontrak yang dapat mengirimkan pesan eksternal ke kontrak tersebut.
  • kedua, ketika kami mengirim pesan yang valid ke TON, kami ingin ini terjadi tepat satu kali dan ketika kami mengirim pesan yang sama lagi, kontrak pintar menolaknya.

Jadi hampir setiap kontrak menyelesaikan kedua masalah ini, karena kontrak kita menerima pesan eksternal, kita juga perlu menanganinya.

Kami akan melakukannya dalam urutan terbalik. Pertama, mari kita selesaikan masalah dengan pengulangan, jika kontrak telah menerima pesan seperti itu dan memprosesnya, maka kontrak tidak akan mengeksekusinya untuk kedua kalinya. Dan kemudian kami akan menyelesaikan masalahnya sehingga hanya kalangan tertentu yang dapat mengirim pesan ke kontrak pintar.

Ada berbagai cara untuk mengatasi masalah pesan duplikat. Inilah cara kami melakukannya. Dalam kontrak pintar, kami menginisialisasi penghitung pesan yang diterima dengan nilai awal 0. Di setiap pesan ke kontrak pintar, kami akan menambahkan nilai penghitung saat ini. Jika nilai penghitung dalam pesan tidak sesuai dengan nilai dalam kontrak pintar, maka kami tidak memprosesnya; jika cocok, maka kami memprosesnya dan menambah penghitung dalam kontrak pintar sebesar 1.

Mari kita kembali ke lottery-test-suite.fif dan tambahkan tes kedua ke dalamnya. Jika kami mengirimkan nomor yang salah, kode tersebut akan memunculkan pengecualian. Misalnya, data kontrak menyimpan 166, dan kami akan mengirimkan 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"

Mari kita luncurkan.

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

Dan kita akan melihat bahwa pengujian dijalankan dengan kesalahan.

[ 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

Pada tahap ini lottery-test-suite.fif seharusnya terlihat seperti ΠΏΠΎ ссылкС.

Sekarang mari tambahkan logika penghitung ke kontrak pintar 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 terletak pesan yang kita kirim.

Hal pertama yang kita lakukan adalah memeriksa apakah pesan tersebut berisi data, jika tidak maka kita keluar saja.

Selanjutnya kita parsing pesannya. in_msg~load_uint(32) memuat nomor 165, 32-bit unsigned int dari pesan yang dikirimkan.

Selanjutnya kita memuat 32 bit dari penyimpanan kontrak pintar. Kami memeriksa apakah nomor yang dimuat cocok dengan nomor yang diteruskan; jika tidak, kami mengeluarkan pengecualian. Dalam kasus kita, karena kita melewatkan non-match, pengecualian harus diberikan.

Sekarang mari kita kompilasi.

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

Salin kode yang dihasilkan ke lottery-test-suite.fif, tidak lupa mengganti baris terakhir.

Kami memeriksa apakah tes tersebut lulus:

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

Di sini Anda dapat melihat komit yang sesuai dengan hasil saat ini.

Perhatikan bahwa tidak nyaman untuk terus-menerus menyalin kode yang dikompilasi dari kontrak pintar ke dalam file dengan pengujian, jadi kami akan menulis skrip yang akan menulis kode tersebut ke dalam konstanta untuk kami, dan kami cukup menghubungkan kode yang dikompilasi ke pengujian kami menggunakan "include".

Buat file di folder proyek build.sh dengan konten berikut.

#!/bin/bash

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

Mari kita buat itu bisa dieksekusi.

chmod +x ./build.sh

Sekarang, jalankan saja skrip kami untuk mengkompilasi kontrak. Tapi selain itu, kita perlu menuliskannya ke dalam konstanta code. Jadi kita akan membuat file baru lotter-compiled-for-test.fif, yang akan kami sertakan dalam file lottery-test-suite.fif.

Mari tambahkan kode rokt ke sh, yang hanya akan menduplikasi file yang dikompilasi lotter-compiled-for-test.fif dan ubah baris terakhir di dalamnya.

# 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

Sekarang, untuk memeriksanya, jalankan skrip yang dihasilkan dan file akan dibuat lottery-compiled-for-test.fif, yang akan kami sertakan dalam kami lottery-test-suite.fif

Π’ lottery-test-suite.fif hapus kode kontrak dan tambahkan baris "lottery-compiled-for-test.fif" include.

Kami menjalankan tes untuk memeriksa apakah mereka lulus.

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

Bagus, sekarang untuk mengotomatiskan peluncuran pengujian, mari buat file test.sh, yang pertama kali akan dieksekusi build.sh, lalu jalankan pengujian.

touch test.sh
chmod +x test.sh

Kami menulis di dalam

./build.sh 

echo "nCompilation completedn"

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

Akan lakukan test.sh dan jalankan untuk memastikan tes berhasil.

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

Kami memeriksa apakah kontrak dikompilasi dan pengujian dijalankan.

Bagus, sekarang sedang memulai test.sh Tes akan dikompilasi dan segera dijalankan. Ini tautannya melakukan.

Oke, sebelum melanjutkan, mari lakukan satu hal lagi demi kenyamanan.

Mari buat folder build di mana kita akan menyimpan kontrak yang disalin dan klonnya ditulis ke dalam sebuah konstanta lottery-compiled.fif, lottery-compiled-for-test.fif. Mari kita juga membuat folder test di mana file pengujian akan disimpan? lottery-test-suite.fif dan kemungkinan file pendukung lainnya. Tautan ke perubahan yang relevan.

Mari terus mengembangkan kontrak pintar.

Selanjutnya harus ada tes yang memeriksa apakah pesan telah diterima dan penghitung diperbarui di toko ketika kami mengirim nomor yang benar. Tapi kita akan melakukannya nanti.

Sekarang mari kita pikirkan struktur data apa dan data apa yang perlu disimpan dalam kontrak pintar.

Saya akan menjelaskan semua yang kami simpan.

`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` пСрСмСнная Ρ‚ΠΈΠΏΠ° ΡΠ»ΠΎΠ²Π°Ρ€ΡŒ, Ρ…Ρ€Π°Π½ΠΈΡ‚ послСдниС Π΄Π²Π°Π΄Ρ†Π°Ρ‚ΡŒ ставок. 

Selanjutnya Anda perlu menulis dua fungsi. Mari kita panggil yang pertama pack_state(), yang akan mengemas data untuk disimpan selanjutnya dalam penyimpanan kontrak pintar. Sebut saja yang kedua unpack_state() akan membaca dan mengembalikan data dari penyimpanan.

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

Kami menambahkan kedua fungsi ini ke awal kontrak pintar. Ini akan berhasil seperti itu hasil antara.

Untuk menyimpan data, Anda perlu memanggil fungsi bawaan set_data() dan itu akan menulis data dari pack_state() dalam penyimpanan kontrak pintar.

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

Sekarang kita memiliki fungsi yang mudah digunakan untuk menulis dan membaca data, kita dapat melanjutkan.

Kita perlu memeriksa apakah pesan yang masuk dari luar ditandatangani oleh pemilik kontrak (atau pengguna lain yang memiliki akses ke kunci privat).

Saat kami menerbitkan kontrak pintar, kami dapat menginisialisasinya dengan data yang kami perlukan dalam penyimpanan, yang akan disimpan untuk penggunaan di masa mendatang. Kami akan mencatat kunci publik di sana sehingga kami dapat memverifikasi bahwa pesan masuk ditandatangani dengan kunci pribadi yang sesuai.

Sebelum melanjutkan, mari buat kunci pribadi dan tuliskan padanya test/keys/owner.pk. Untuk melakukan ini, mari luncurkan Fift dalam mode interaktif dan jalankan empat perintah.

`newkeypair` гСнСрация ΠΏΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΈ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π° ΠΈ запись ΠΈΡ… Π² стСк. 

`drop` удалСния ΠΈΠ· стСка Π²Π΅Ρ€Ρ…Π½Π΅Π³ΠΎ элСмСнта (Π² Π΄Π°Π½Π½ΠΎΠΌ случаС ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡)  

`.s` просто ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ Π»Π΅ΠΆΠΈΡ‚ Π² стСкС Π² Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ 

`"owner.pk" B>file` запись ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π° Π² Ρ„Π°ΠΉΠ» с ΠΈΠΌΠ΅Π½Π΅ΠΌ `owner.pk`. 

`bye` Π·Π°Π²Π΅Ρ€ΡˆΠ°Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Ρƒ с Fift. 

Mari buat folder keys di dalam folder test dan tulis kunci pribadi di sana.

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

Kami melihat file di folder saat ini owner.pk.

Kami menghapus kunci publik dari tumpukan dan bila diperlukan kami bisa mendapatkannya dari kunci pribadi.

Sekarang kita perlu menulis verifikasi tanda tangan. Mari kita mulai dengan tesnya. Pertama kita membaca kunci pribadi dari file menggunakan fungsi file>B dan menuliskannya ke variabel owner_private_key, lalu gunakan fungsinya priv>pub ubah kunci pribadi menjadi kunci publik dan tulis hasilnya 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 !

Kami membutuhkan kedua kunci tersebut.

Kami menginisialisasi penyimpanan kontrak pintar dengan data arbitrer dalam urutan yang sama seperti pada fungsinya pack_state()dan menuliskannya ke dalam 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 !

Selanjutnya kita akan membuat pesan yang ditandatangani, itu hanya berisi tanda tangan dan nilai counter.

Pertama, kita membuat data yang ingin kita kirimkan, lalu kita menandatanganinya dengan kunci pribadi dan terakhir kita membuat pesan yang ditandatangani.

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 !  

Hasilnya, pesan yang akan kami kirim ke kontrak pintar dicatat dalam sebuah variabel message_to_send, tentang fungsi hashu, ed25519_sign_uint bisa membaca dalam dokumentasi Fift.

Dan untuk menjalankan tes kami menelepon lagi.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

Disini begitu File dengan tes akan terlihat seperti ini pada tahap ini.

Mari kita jalankan pengujiannya dan itu akan gagal, jadi kita akan mengubah kontrak pintar sehingga dapat menerima pesan dalam format ini dan memverifikasi tanda tangannya.

Pertama, kita menghitung 512 bit tanda tangan dari pesan dan menuliskannya ke sebuah variabel, kemudian kita menghitung 32 bit variabel counter.

Karena kami memiliki fungsi untuk membaca data dari penyimpanan kontrak pintar, kami akan menggunakannya.

Selanjutnya dilakukan pengecekan counter yang ditransfer dengan tempat penyimpanan dan pengecekan tanda tangan. Jika ada sesuatu yang tidak cocok, maka kami memberikan pengecualian dengan kode yang sesuai.

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

Komitmen yang relevan di sini.

Mari kita jalankan pengujian dan lihat apakah pengujian kedua gagal. Karena dua alasan, jumlah bit dalam pesan tidak mencukupi dan jumlah bit dalam penyimpanan tidak mencukupi, sehingga kode mengalami error saat penguraian. Kita perlu menambahkan tanda tangan pada pesan yang kita kirim dan menyalin penyimpanan dari pengujian terakhir.

Pada pengujian kedua, kami akan menambahkan tanda tangan pesan dan mengubah penyimpanan kontrak pintar. Disini begitu file dengan tes terlihat seperti saat ini.

Mari kita menulis tes keempat, di mana kita akan mengirimkan pesan yang ditandatangani dengan kunci pribadi orang lain. Mari buat kunci pribadi lain dan simpan ke file not-owner.pk. Kami akan menandatangani pesan dengan kunci pribadi ini. Mari kita jalankan tes dan pastikan semua tes lulus. Melakukan saat ini.

Sekarang kita akhirnya bisa beralih ke penerapan logika kontrak pintar.
Π’ recv_external() kami akan menerima dua jenis pesan.

Karena kontrak kami akan mengakumulasi kerugian para pemain, uang ini harus ditransfer ke pencipta lotere. Alamat dompet pembuat lotere dicatat dalam penyimpanan saat kontrak dibuat.

Untuk berjaga-jaga, kita memerlukan kemampuan untuk mengubah alamat tujuan pengiriman gram kepada yang kalah. Kita juga harus bisa mengirimkan gram hasil undian ke alamat pemiliknya.

Mari kita mulai dengan yang pertama. Pertama-tama mari kita tulis tes yang akan memeriksa bahwa setelah mengirim pesan, kontrak pintar menyimpan alamat baru di penyimpanan. Harap dicatat bahwa dalam pesan tersebut, selain loket dan alamat baru, kami juga mengirimkan action Bilangan bulat non-negatif 7-bit, bergantung padanya, kami akan memilih cara memproses pesan dalam kontrak pintar.

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

Dalam pengujian tersebut Anda dapat melihat bagaimana penyimpanan smartcontract dideserialisasi storage di Lima. Deserialisasi variabel dijelaskan dalam dokumentasi Fift.

Tautan komit dengan tambahan adonan.

Mari kita jalankan pengujian dan pastikan gagal. Sekarang mari tambahkan logika untuk mengubah alamat pemilik lotere.

Dalam kontrak pintar kami terus mengurai message, bacalah action. Izinkan kami mengingatkan Anda bahwa kami akan memiliki dua action: ganti alamat dan kirim gram.

Kemudian kita membaca alamat baru pemilik kontrak dan menyimpannya di penyimpanan.
Kami menjalankan pengujian dan melihat bahwa pengujian ketiga gagal. Itu macet karena fakta bahwa kontrak sekarang juga mem-parsing 7 bit dari pesan, yang hilang dalam pengujian. Tambahkan yang tidak ada ke pesan action. Mari kita jalankan pengujian dan lihat apakah semuanya berhasil. Di sini berkomitmen terhadap perubahan. Besar.

Sekarang mari kita tuliskan logika untuk mengirimkan jumlah gram yang ditentukan ke alamat yang disimpan sebelumnya.

Pertama, mari kita menulis tes. Kami akan menulis dua tes, satu ketika saldo tidak cukup, yang kedua ketika semuanya harus berhasil. Tes dapat dilihat dalam komitmen ini.

Sekarang mari tambahkan kodenya. Pertama, mari kita tulis dua metode pembantu. Metode dapatkan pertama adalah mengetahui saldo kontrak pintar saat ini.

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

Dan yang kedua adalah untuk mengirim gram ke kontrak pintar lain. Saya sepenuhnya menyalin metode ini dari kontrak pintar lainnya.

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

Mari tambahkan kedua metode ini ke kontrak pintar dan tulis logikanya. Pertama, kita parsing jumlah gram dari pesan tersebut. Selanjutnya kita cek saldo, jika kurang kita keluarkan pengecualian. Jika semuanya baik-baik saja, maka kami mengirim gram ke alamat yang disimpan dan memperbarui penghitung.

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

Disini begitu sepertinya kontrak pintar saat ini. Mari kita jalankan tesnya dan pastikan mereka lulus.

Omong-omong, komisi dipotong dari kontrak pintar setiap kali pesan diproses. Agar pesan kontrak pintar dapat menjalankan permintaan, setelah pemeriksaan dasar Anda perlu menelepon accept_message().

Sekarang mari beralih ke pesan internal. Faktanya, kami hanya akan menerima gram dan mengirimkan kembali dua kali lipat jumlahnya kepada pemain jika dia menang dan sepertiganya kepada pemilik jika dia kalah.

Pertama, mari kita menulis tes sederhana. Untuk melakukan ini, kami memerlukan alamat uji kontrak pintar yang seharusnya kami kirimkan gramnya ke kontrak pintar.

Alamat kontrak pintar terdiri dari dua angka, bilangan bulat 32-bit yang bertanggung jawab atas rantai kerja dan nomor akun unik bilangan bulat non-negatif 256-bit dalam rantai kerja ini. Misalnya -1 dan 12345, ini adalah alamat yang akan kita simpan ke sebuah file.

Saya menyalin fungsi untuk menyimpan alamat 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

Mari kita lihat cara kerjanya, ini akan memberikan pemahaman tentang cara kerja Fift. Luncurkan Fift dalam mode interaktif.

~/TON/build/crypto/fift -i 

Pertama kita tekan -1, 12345 dan nama file masa depan "sender.addr" ke dalam tumpukan:

-1 12345 "sender.addr" 

Langkah selanjutnya adalah menjalankan fungsinya -rot, yang menggeser tumpukan sedemikian rupa sehingga di bagian atas tumpukan terdapat nomor kontrak pintar yang unik:

"sender.addr" -1 12345

256 u>B mengonversi bilangan bulat non-negatif 256-bit menjadi byte.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap menukar dua elemen teratas tumpukan.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B mengonversi bilangan bulat 32-bit menjadi byte.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ menghubungkan dua urutan byte.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

Lagi swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

Dan akhirnya byte ditulis ke file B>file. Setelah ini tumpukan kami kosong. Kami berhenti Fift. Sebuah file telah dibuat di folder saat ini sender.addr. Mari kita pindahkan file ke folder yang dibuat test/addresses/.

Mari kita tulis tes sederhana yang akan mengirimkan gram ke kontrak pintar. Inilah komitmennya.

Sekarang mari kita lihat logika lotere.

Hal pertama yang kami lakukan adalah memeriksa pesannya bounced atau tidak jika bounced, maka kita mengabaikannya. bounced berarti kontrak akan mengembalikan gram jika terjadi kesalahan. Kami tidak akan mengembalikan gram jika tiba-tiba terjadi kesalahan.

Kita cek, kalau saldonya kurang dari setengah gram, maka kita terima saja pesannya dan abaikan saja.

Selanjutnya, kami menguraikan alamat kontrak pintar dari mana pesan tersebut berasal.

Kami membaca data dari penyimpanan dan kemudian menghapus taruhan lama dari riwayat jika jumlahnya lebih dari dua puluh. Untuk kenyamanan, saya menulis tiga fungsi tambahan pack_order(), unpack_order(), remove_old_orders().

Selanjutnya kita lihat apakah saldo tidak cukup untuk pembayaran, maka kita anggap ini bukan taruhan, melainkan pengisian ulang dan simpan pengisian tersebut di orders.

Lalu yang terakhir inti dari kontrak pintar.

Pertama, jika pemain kalah, kami menyimpannya di riwayat taruhan dan jika jumlahnya lebih dari 3 gram, kami mengirimkan 1/3 ke pemilik kontrak pintar.

Jika pemain menang, maka kami mengirimkan jumlahnya dua kali lipat ke alamat pemain dan kemudian menyimpan informasi tentang taruhan tersebut di riwayat.

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

Itu saja. Komit yang sesuai.

Sekarang yang tersisa hanyalah sederhana, mari buat metode get sehingga kita dapat memperoleh informasi tentang status kontrak dari dunia luar (bahkan, membaca data dari penyimpanan kontrak pintar mereka).

Mari tambahkan metode get. Kami akan menulis di bawah ini tentang cara menerima informasi tentang kontrak pintar.

Saya juga lupa menambahkan kode yang akan memproses permintaan pertama yang terjadi saat menerbitkan kontrak pintar. Komit yang sesuai. Dan selanjutnya dikoreksi bug dengan mengirimkan 1/3 dari jumlah ke rekening pemilik.

Langkah selanjutnya adalah mempublikasikan kontrak pintar. Mari buat folder requests.

Saya mengambil kode publikasi sebagai dasar simple-wallet-code.fc yang dapat menemukan di repositori resmi.

Sesuatu yang patut diperhatikan. Kami menghasilkan penyimpanan kontrak pintar dan pesan masukan. Setelah ini, alamat kontrak pintar dibuat, yaitu alamat tersebut diketahui bahkan sebelum dipublikasikan di TON. Maka Anda perlu mengirim beberapa gram ke alamat ini, dan hanya setelah itu Anda perlu mengirim file dengan kontrak pintar itu sendiri, karena jaringan mengambil komisi untuk menyimpan kontrak pintar dan operasi di dalamnya (validator yang menyimpan dan mengeksekusi kontrak pintar ). Kodenya dapat dilihat di sini.

Selanjutnya kita jalankan kode penerbitan dan dapatkan lottery-query.boc file dan alamat kontrak pintar.

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

Jangan lupa untuk menyimpan file yang dihasilkan: lottery-query.boc, lottery.addr, lottery.pk.

Antara lain, kita akan melihat alamat kontrak pintar di log eksekusi.

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

Sekadar iseng, ayo buat permintaan ke TON

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

Dan kita akan melihat bahwa akun dengan alamat ini kosong.

account state is empty

Kami mengirim ke alamatnya 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram dan setelah beberapa detik kami menjalankan perintah yang sama. Untuk mengirim gram saya menggunakan dompet resmi, dan Anda dapat meminta seseorang dari obrolan untuk menguji gram, yang akan saya bicarakan di akhir artikel.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Sepertinya tidak diinisialisasi (state:account_uninit) kontrak pintar dengan alamat yang sama dan saldo 1 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

Sekarang mari kita publikasikan kontrak pintar. Mari luncurkan klien ringan dan jalankan.

> 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 

Mari kita periksa apakah kontraknya telah diterbitkan.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

Antara lain yang kami dapatkan.

  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

Kami melihat itu account_active.

Komit yang sesuai dengan perubahan di sini.

Sekarang mari buat permintaan untuk berinteraksi dengan kontrak pintar.

Lebih tepatnya, yang pertama akan kami tinggalkan untuk mengubah alamat sebagai pekerjaan mandiri, dan kami akan melakukan yang kedua untuk mengirim gram ke alamat pemiliknya. Faktanya, kita perlu melakukan hal yang sama seperti pada tes pengiriman gram.

Ini adalah pesan yang akan kami kirimkan ke kontrak pintar, di mana msg_seqno 165, action 2 dan 9.5 gram untuk pengiriman.

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

Jangan lupa untuk menandatangani pesan dengan kunci pribadi Anda lottery.pk, yang dihasilkan sebelumnya saat membuat kontrak pintar. Ini adalah komit yang sesuai.

Menerima informasi dari kontrak pintar menggunakan metode get

Sekarang mari kita lihat cara menjalankan metode get kontrak pintar.

Meluncurkan lite-client dan jalankan metode get yang kami tulis.

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

Π’ result berisi nilai yang dikembalikan fungsi balance() dari kontrak pintar kami.
Kami akan melakukan hal yang sama untuk beberapa metode lainnya.

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

Mari kita tanyakan riwayat taruhan Anda.

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

Kami akan menggunakan klien ringan dan mendapatkan metode untuk menampilkan informasi tentang kontrak pintar di situs.

Menampilkan data kontrak pintar di situs web

Saya menulis situs web sederhana dengan Python untuk menampilkan data dari kontrak pintar dengan cara yang nyaman. Di sini saya tidak akan membahasnya secara detail dan akan mempublikasikan situsnya dalam satu komit.

Permintaan ke TON dibuat dari Python melalui lite-client. Demi kenyamanan, situs ini dikemas dalam Docker dan dipublikasikan di Google Cloud. Tautan.

Mencoba

Sekarang mari kita coba mengirim gram ke sana untuk diisi ulang dompet. Kami akan mengirimkan 40 gram. Dan mari kita membuat beberapa taruhan untuk kejelasan. Kami melihat bahwa situs tersebut menampilkan riwayat taruhan, persentase kemenangan saat ini, dan informasi berguna lainnya.

Kami melihatbahwa kami menang pada yang pertama, kalah pada yang kedua.

penutup

Artikelnya ternyata lebih panjang dari yang saya harapkan, mungkin bisa lebih pendek, atau mungkin hanya untuk orang yang tidak tahu apa-apa tentang TON dan ingin menulis dan menerbitkan kontrak pintar yang tidak terlalu sederhana dengan kemampuan untuk berinteraksi dengan dia. Mungkin ada beberapa hal yang bisa dijelaskan dengan lebih sederhana.

Mungkin beberapa aspek penerapannya dapat dilakukan dengan lebih efisien dan elegan, namun akan memerlukan lebih banyak waktu untuk menyiapkan artikelnya. Mungkin juga saya membuat kesalahan di suatu tempat atau tidak memahami sesuatu, jadi jika Anda melakukan sesuatu yang serius, Anda perlu mengandalkan dokumentasi resmi atau repositori resmi dengan kode TON.

Perlu dicatat bahwa karena TON sendiri masih dalam tahap pengembangan aktif, perubahan mungkin terjadi yang akan menghentikan langkah mana pun dalam artikel ini (yang terjadi saat saya menulis, sudah diperbaiki), tetapi pendekatan umumnya adalah kecil kemungkinannya untuk berubah.

Saya tidak akan membicarakan masa depan TON. Mungkin platform ini akan menjadi sesuatu yang besar dan kita harus meluangkan waktu untuk mempelajarinya dan mengisi ceruk pasar dengan produk kita sekarang.

Ada juga Libra dari Facebook yang memiliki potensi pengguna lebih besar dari TON. Saya hampir tidak tahu apa-apa tentang Libra, dilihat dari forumnya, ada lebih banyak aktivitas di sana daripada di komunitas TON. Meskipun pengembang dan komunitas TON lebih menyukai underground, itu juga keren.

referensi

  1. Dokumentasi resmi TON: https://test.ton.org
  2. Repositori TON resmi: https://github.com/ton-blockchain/ton
  3. Dompet resmi untuk berbagai platform: https://wallet.ton.org
  4. Repositori kontrak pintar dari artikel ini: https://github.com/raiym/astonished
  5. Tautan ke situs web kontrak pintar: https://ton-lottery.appspot.com
  6. Repositori untuk ekstensi Visual Studio Code for FunC: https://github.com/raiym/func-visual-studio-plugin
  7. Ngobrol tentang TON di Telegram, yang sangat membantu untuk memahaminya pada tahap awal. Saya pikir tidak salah jika saya mengatakan bahwa semua orang yang menulis sesuatu untuk TON ada di sana. Anda juga bisa meminta tes gram di sana. https://t.me/tondev_ru
  8. Obrolan lain tentang TON di mana saya menemukan informasi berguna: https://t.me/TONgramDev
  9. Tahap pertama kompetisi: https://contest.com/blockchain
  10. Kompetisi tahap kedua: https://contest.com/blockchain-2

Sumber: www.habr.com

Tambah komentar